﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.IO;
using Kokomo.Lexical.Expressions;
using Kokomo.Lexical.Coding;
using System.Diagnostics;
using LinqExpressions = System.Linq.Expressions;
using LinqExpression = System.Linq.Expressions.Expression;
using Kokomo.PeInspector.ObjectGraphs;
using Kokomo.PeInspector.Reflection.Disassembly.Flow;
using Kokomo.Mirror.CliMetadata.Signatures;
using Kokomo.Mirror.CliMetadata;
using System.Runtime.InteropServices;

namespace Kokomo.PeInspector.Reflection.Disassembly
{
	public static class MsilDecompiler
	{
		private static bool Equal(MemberInfo x, MemberInfo y)
		{
			return MemberEqualityComparer.Instance.Equals(x, y);
		}

		#region Opcode Handlers
		private static Dictionary<short, OpcodeHandler> OpcodeHandlerTable;

		#region I/O count
		private static int CountNewObjectInputs(MethodBase methodBase, object operand)
		{
			ConstructorInfo constructor = (ConstructorInfo)operand;
			return constructor.GetParameters().Length;
		}

		private static int CountCallInputs(MethodBase methodBase, object operand)
		{
			MethodBase targetMethodBase = (MethodBase)operand;
			int parameterCount = targetMethodBase.GetParameters().Length;
			if (!methodBase.IsStatic)
			{
				parameterCount++;
			}

			return parameterCount;
		}
		private static int CountCallOutputs(MethodBase methodBase, object operand)
		{
			MethodInfo method = operand as MethodInfo;
			int outputCount =
				((method != null) && (method.ReturnType != typeof(void))) ? 1 : 0
				;

			return outputCount;
		}
		private static int CountReturnInputs(MethodBase methodBase, object operand)
		{
			MethodInfo method = methodBase as MethodInfo;
			int inputCount =
				((method != null) && (method.ReturnType != typeof(void))) ? 1 : 0
				;

			return inputCount;
		}

		private static int CountCallIndirectInputs(MethodBase methodBase, object operand)
		{
			MethodRefSignature methodRefSignature = SignatureReader.ReadMethodRefSignature((byte[])operand);
			int outputCount =
				methodRefSignature.HasReturnValue() ? 1 : 0
				;

			return outputCount;
		}
		private static int CountCallIndirectOutputs(MethodBase methodBase, object operand)
		{
			MethodRefSignature methodRefSignature = SignatureReader.ReadMethodRefSignature((byte[])operand);
			int inputCount = methodRefSignature.parameters.Length;

			return inputCount;
		}
		#endregion

		#region Dependencies
		private static IEnumerable<MsilDependency> GetLoadStringDependencies(MethodBase method, object operand)
		{
			return new MsilDependency[] {
				new MsilStringDependency(method.Module, (string)operand, 0)
			};
		}
		private static IEnumerable<MsilDependency> GetLoadTokenDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			MetadataTableNumber tableNumber = CodedIndex.TableFromToken(memberRef.MetadataToken);
			MsilDependency dependency = null;
			switch (tableNumber)
			{
				case MetadataTableNumber.TypeDef:
					dependency = MsilTypeDependency.FromTypeRef(memberRef, MsilTypeUsage.Reference);
					break;
				case MetadataTableNumber.MethodDef:
					dependency = MsilMethodDependency.FromMethodRef(memberRef, MsilMethodUsage.Reference);
					break;
				case MetadataTableNumber.Field:
					dependency = MsilFieldDependency.FromFieldRef(memberRef, MsilVariableReferenceType.Reference);
					break;
			}

			return (dependency != null) ? new MsilDependency[] { dependency } : new MsilDependency[0];
		}
		private static IEnumerable<MsilDependency> GetNewObjectDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			if (memberRef.IsCanonical)
			{
				MethodBase targetMethod = (MethodBase)memberRef.LoadMember();
				return new MsilDependency[]{
				MsilMethodDependency.FromMethod(targetMethod, MsilMethodUsage.Invoke),
				MsilTypeDependency.FromType(targetMethod.DeclaringType, MsilTypeUsage.Instantiation)
			};
			}
			else
			{
				return new MsilDependency[0];
			}
		}
		private static IEnumerable<MsilDependency> GetThrowDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			if (memberRef.IsCanonical)
			{
				MethodBase targetMethod = (MethodBase)memberRef.LoadMember();
				return new MsilDependency[]{
				MsilMethodDependency.FromMethod(targetMethod, MsilMethodUsage.Invoke),
				MsilTypeDependency.FromType(targetMethod.DeclaringType, MsilTypeUsage.Instantiation),
				MsilTypeDependency.FromType(targetMethod.DeclaringType, MsilTypeUsage.Thrown)
			};
			}
			else
			{
				return new MsilDependency[0];
			}
		}
		private static IEnumerable<MsilDependency> GetCallDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			//MethodBase targetMethod = (MethodBase)operand;

			List<MsilDependency> dependencies = new List<MsilDependency>(2);
			dependencies.Add(MsilMethodDependency.FromMethodRef(memberRef, MsilMethodUsage.Invoke));
			//if (targetMethod.DeclaringType != null) dependencies.Add(MsilTypeDependency.FromType(targetMethod.DeclaringType, MsilTypeUsage.MemberAccess));
			//{
			//    MethodInfo methodInfo = targetMethod as MethodInfo;
			//    if (methodInfo != null) dependencies.Add(MsilTypeDependency.FromType(methodInfo.ReturnType, MsilTypeUsage.Reference));
			//}

			return dependencies;
		}
		private static IEnumerable<MsilDependency> GetLoadFunctionDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			//MethodBase targetMethod = (MethodBase)operand;

			List<MsilDependency> dependencies = new List<MsilDependency>(2);
			dependencies.Add(MsilMethodDependency.FromMethodRef(memberRef, MsilMethodUsage.Reference));
			//if (targetMethod.DeclaringType != null) dependencies.Add(MsilTypeDependency.FromType(targetMethod.DeclaringType, MsilTypeUsage.MemberAccess));

			return dependencies;
		}
		private static IEnumerable<MsilDependency> GetNewArrayDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			//Type elementType = (Type)operand;
			return new MsilDependency[] {
				MsilTypeDependency.FromTypeRef(memberRef, MsilTypeUsage.Array)
			};
		}
		private static IEnumerable<MsilDependency> GetLoadFieldDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			//FieldInfo field = (FieldInfo)operand;

			List<MsilDependency> dependencies = new List<MsilDependency>(2);
			dependencies.Add(MsilFieldDependency.FromFieldRef(memberRef, MsilVariableReferenceType.Load));
			//if (field.DeclaringType != null) dependencies.Add(MsilTypeDependency.FromType(field.DeclaringType, MsilTypeUsage.MemberAccess));

			return dependencies;
		}
		private static IEnumerable<MsilDependency> GetLoadFieldIndirectDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			//FieldInfo field = (FieldInfo)operand;

			List<MsilDependency> dependencies = new List<MsilDependency>(2);
			dependencies.Add(MsilFieldDependency.FromFieldRef(memberRef, MsilVariableReferenceType.Reference));
			//if (field.DeclaringType != null) dependencies.Add(MsilTypeDependency.FromType(field.DeclaringType, MsilTypeUsage.MemberAccess));

			return dependencies;
		}
		private static IEnumerable<MsilDependency> GetStoreFieldDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			//FieldInfo field = (FieldInfo)operand;

			List<MsilDependency> dependencies = new List<MsilDependency>(2);
			dependencies.Add(MsilFieldDependency.FromFieldRef(memberRef, MsilVariableReferenceType.Store));
			//if (field.DeclaringType != null) dependencies.Add(MsilTypeDependency.FromType(field.DeclaringType, MsilTypeUsage.MemberAccess));

			return dependencies;
		}
		private static IEnumerable<MsilDependency> GetConversionDependencies(MethodBase method, object operand)
		{
			ModuleMemberReference memberRef = (ModuleMemberReference)operand;
			//Type type = (Type)operand;
			return new MsilDependency[]{
				MsilTypeDependency.FromTypeRef(memberRef,MsilTypeUsage.Conversion)
			};
		}
		#endregion

		private static void InitializeOpCodeHandlers()
		{
			List<OpcodeHandler> handlers = new List<OpcodeHandler>();

			handlers.Add(new OpcodeHandler(OpCodes.Nop, HandleNop));
			handlers.Add(new OpcodeHandler(OpCodes.Pop, HandlePop));
			handlers.Add(new OpcodeHandler(OpCodes.Ret, HandleReturn, CountReturnInputs));
			handlers.Add(new OpcodeHandler(OpCodes.Dup, HandleDuplicate));
			handlers.Add(new OpcodeHandler(OpCodes.Arglist, HandleArgList));
			handlers.Add(new OpcodeHandler(OpCodes.Mkrefany, HandleNop));
			handlers.Add(new OpcodeHandler(OpCodes.Refanytype, HandleNop));
			handlers.Add(new OpcodeHandler(OpCodes.Refanyval, HandleNop));
			handlers.Add(new OpcodeHandler(OpCodes.Localloc, HandleLocAlloc));
			// Constants
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4, HandleLoadConstant));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_0, (context, operand) => HandleLoadConstant(context, 0)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_1, (context, operand) => HandleLoadConstant(context, 1)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_2, (context, operand) => HandleLoadConstant(context, 2)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_3, (context, operand) => HandleLoadConstant(context, 3)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_4, (context, operand) => HandleLoadConstant(context, 4)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_5, (context, operand) => HandleLoadConstant(context, 5)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_6, (context, operand) => HandleLoadConstant(context, 6)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_7, (context, operand) => HandleLoadConstant(context, 7)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_8, (context, operand) => HandleLoadConstant(context, 8)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_M1, (context, operand) => HandleLoadConstant(context, -1)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I4_S, HandleLoadConstantShort));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_I8, HandleLoadConstant));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_R4, HandleLoadConstant));
			handlers.Add(new OpcodeHandler(OpCodes.Ldc_R8, HandleLoadConstant));
			handlers.Add(new OpcodeHandler(OpCodes.Ldstr, HandleLoadString, dependencyDelegate: GetLoadStringDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Ldnull, HandleLoadConstant));
			handlers.Add(new OpcodeHandler(OpCodes.Ldtoken, HandleLoadToken, dependencyDelegate: GetLoadTokenDependencies));
			// Operators
			handlers.Add(new OpcodeHandler(OpCodes.Add, HandleAdd));
			handlers.Add(new OpcodeHandler(OpCodes.Add_Ovf, HandleAdd));
			handlers.Add(new OpcodeHandler(OpCodes.Add_Ovf_Un, HandleAdd));
			handlers.Add(new OpcodeHandler(OpCodes.Sub, HandleSubtract));
			handlers.Add(new OpcodeHandler(OpCodes.Sub_Ovf, HandleSubtract));
			handlers.Add(new OpcodeHandler(OpCodes.Sub_Ovf_Un, HandleSubtract));
			handlers.Add(new OpcodeHandler(OpCodes.Mul, HandleMultiply));
			handlers.Add(new OpcodeHandler(OpCodes.Mul_Ovf, HandleMultiply));
			handlers.Add(new OpcodeHandler(OpCodes.Mul_Ovf_Un, HandleMultiply));
			handlers.Add(new OpcodeHandler(OpCodes.Div, HandleDivide));
			handlers.Add(new OpcodeHandler(OpCodes.Div_Un, HandleDivide));
			handlers.Add(new OpcodeHandler(OpCodes.Rem, HandleRemainder));
			handlers.Add(new OpcodeHandler(OpCodes.Rem_Un, HandleRemainder));
			handlers.Add(new OpcodeHandler(OpCodes.Neg, HandleNegation));
			handlers.Add(new OpcodeHandler(OpCodes.And, HandleBitwiseAnd));
			handlers.Add(new OpcodeHandler(OpCodes.Or, HandleBitwiseOr));
			handlers.Add(new OpcodeHandler(OpCodes.Xor, HandleBitwiseExclusiveOr));
			handlers.Add(new OpcodeHandler(OpCodes.Not, HandleBitwiseComplement));
			handlers.Add(new OpcodeHandler(OpCodes.Shl, HandleShiftLeft));
			handlers.Add(new OpcodeHandler(OpCodes.Shr, HandleShiftRight));
			handlers.Add(new OpcodeHandler(OpCodes.Shr_Un, HandleShiftRight));
			handlers.Add(new OpcodeHandler(OpCodes.Ceq, HandleCheckEquality));
			handlers.Add(new OpcodeHandler(OpCodes.Cgt, HandleCheckGreaterThan));
			handlers.Add(new OpcodeHandler(OpCodes.Cgt_Un, HandleCheckGreaterThan));
			handlers.Add(new OpcodeHandler(OpCodes.Clt, HandleCheckLessThan));
			handlers.Add(new OpcodeHandler(OpCodes.Clt_Un, HandleCheckLessThan));
			// Objects
			handlers.Add(new OpcodeHandler(OpCodes.Newobj, HandleNewObject, CountNewObjectInputs, null, GetNewObjectDependencies));
			// Method calls
			handlers.Add(new OpcodeHandler(OpCodes.Call, HandleCall, CountCallInputs, CountCallOutputs, GetCallDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Calli, HandleCallIndirect, CountCallIndirectInputs, CountCallIndirectOutputs));
			handlers.Add(new OpcodeHandler(OpCodes.Callvirt, HandleCall, CountCallInputs, CountCallOutputs, GetCallDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Ldftn, HandleLoadFunction, dependencyDelegate: GetLoadFunctionDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Ldvirtftn, HandleLoadVirtualFunction, dependencyDelegate: GetLoadFunctionDependencies));
			// Arrays
			handlers.Add(new OpcodeHandler(OpCodes.Newarr, HandleNewArray, dependencyDelegate: GetNewArrayDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Ldlen, HandleLoadArrayLength));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelema, HandleLoadElementAddress));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_I, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_I1, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_I2, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_I4, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_I8, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_R4, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_R8, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_Ref, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_U1, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_U2, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Ldelem_U4, HandleLoadElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_I, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_I1, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_I2, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_I4, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_I8, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_R4, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_R8, HandleStoreElement));
			handlers.Add(new OpcodeHandler(OpCodes.Stelem_Ref, HandleStoreElement));
			// Variables
			handlers.Add(new OpcodeHandler(OpCodes.Ldloc, HandleLoadLocal));
			handlers.Add(new OpcodeHandler(OpCodes.Ldloc_0, (context, operand) => HandleLoadLocal(context, 0)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldloc_1, (context, operand) => HandleLoadLocal(context, 1)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldloc_2, (context, operand) => HandleLoadLocal(context, 2)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldloc_3, (context, operand) => HandleLoadLocal(context, 3)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldloca, HandleLoadLocalAddress));
			handlers.Add(new OpcodeHandler(OpCodes.Ldloc_S, HandleLoadLocalShort));
			handlers.Add(new OpcodeHandler(OpCodes.Ldloca_S, HandleLoadLocalAddressShort));
			handlers.Add(new OpcodeHandler(OpCodes.Stloc, HandleStoreLocal));
			handlers.Add(new OpcodeHandler(OpCodes.Stloc_0, (context, operand) => HandleStoreLocal(context, 0)));
			handlers.Add(new OpcodeHandler(OpCodes.Stloc_1, (context, operand) => HandleStoreLocal(context, 1)));
			handlers.Add(new OpcodeHandler(OpCodes.Stloc_2, (context, operand) => HandleStoreLocal(context, 2)));
			handlers.Add(new OpcodeHandler(OpCodes.Stloc_3, (context, operand) => HandleStoreLocal(context, 3)));
			handlers.Add(new OpcodeHandler(OpCodes.Stloc_S, HandleStoreLocalShort));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarg, HandleLoadArgument));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarg_0, (context, operand) => HandleLoadArgument(context, 0)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarg_1, (context, operand) => HandleLoadArgument(context, 1)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarg_2, (context, operand) => HandleLoadArgument(context, 2)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarg_3, (context, operand) => HandleLoadArgument(context, 3)));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarga, HandleLoadArgumentAddress));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarg_S, HandleLoadArgumentShort));
			handlers.Add(new OpcodeHandler(OpCodes.Ldarga_S, HandleLoadArgumentAddressShort));
			handlers.Add(new OpcodeHandler(OpCodes.Starg, HandleStoreArgument));
			handlers.Add(new OpcodeHandler(OpCodes.Starg_S, HandleStoreArgumentShort));
			// References
			handlers.Add(new OpcodeHandler(OpCodes.Initobj, HandleInitializeObject));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_I, HandleLoadIndirectIntPtr));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_I1, HandleLoadIndirectSByte));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_I2, HandleLoadIndirectInt16));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_I4, HandleLoadIndirectInt));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_I8, HandleLoadIndirectLong));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_R4, HandleLoadIndirectSingle));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_R8, HandleLoadIndirectDouble));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_U1, HandleLoadIndirectByte));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_U2, HandleLoadIndirectUInt16));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_U4, HandleLoadIndirectUInt));
			handlers.Add(new OpcodeHandler(OpCodes.Ldind_Ref, HandleLoadIndirectRef));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_I, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_I1, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_I2, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_I4, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_I8, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_R4, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_R8, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Stind_Ref, HandleStoreIndirect));
			handlers.Add(new OpcodeHandler(OpCodes.Ldobj, HandleLoadObject));
			handlers.Add(new OpcodeHandler(OpCodes.Stobj, HandleStoreIndirect));
			// Fields
			handlers.Add(new OpcodeHandler(OpCodes.Ldfld, HandleLoadField, dependencyDelegate: GetLoadFieldDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Ldflda, HandleLoadFieldAddress, dependencyDelegate: GetLoadFieldIndirectDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Ldsfld, HandleLoadStaticField, dependencyDelegate: GetLoadFieldDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Ldsflda, HandleLoadStaticFieldAddress, dependencyDelegate: GetLoadFieldIndirectDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Stfld, HandleStoreField, dependencyDelegate: GetStoreFieldDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Stsfld, HandleStoreStaticField, dependencyDelegate: GetStoreFieldDependencies));
			// Conversion
			handlers.Add(new OpcodeHandler(OpCodes.Conv_I, HandleConvertIntPtr));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I, HandleConvertIntPtr));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I_Un, HandleConvertIntPtr));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_I1, HandleConvertByte));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I1, HandleConvertByte));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I1_Un, HandleConvertByte));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_I2, HandleConvertShort));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I2, HandleConvertShort));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I2_Un, HandleConvertShort));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_I4, HandleConvertInt));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I4, HandleConvertInt));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I4_Un, HandleConvertInt));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_I8, HandleConvertLong));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I8, HandleConvertLong));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_I8_Un, HandleConvertLong));

			handlers.Add(new OpcodeHandler(OpCodes.Conv_U, HandleConvertUIntPtr));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U, HandleConvertUIntPtr));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U_Un, HandleConvertUIntPtr));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_U1, HandleConvertUByte));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U1, HandleConvertUByte));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U1_Un, HandleConvertUByte));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_U2, HandleConvertUShort));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U2, HandleConvertUShort));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U2_Un, HandleConvertUShort));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_U4, HandleConvertUInt));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U4, HandleConvertUInt));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U4_Un, HandleConvertUInt));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_U8, HandleConvertULong));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U8, HandleConvertULong));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_Ovf_U8_Un, HandleConvertULong));


			handlers.Add(new OpcodeHandler(OpCodes.Conv_R_Un, HandleConvertReal32));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_R4, HandleConvertReal32));
			handlers.Add(new OpcodeHandler(OpCodes.Conv_R8, HandleConvertReal64));
			handlers.Add(new OpcodeHandler(OpCodes.Castclass, HandleCast, dependencyDelegate: GetConversionDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Isinst, HandleIsInstance, dependencyDelegate: GetConversionDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Box, HandleNop, dependencyDelegate: GetConversionDependencies));
			handlers.Add(new OpcodeHandler(OpCodes.Unbox_Any, HandleCast, dependencyDelegate: GetConversionDependencies));
			// Branching
			handlers.Add(new OpcodeHandler(OpCodes.Brtrue, HandleBranchTrue));
			handlers.Add(new OpcodeHandler(OpCodes.Brtrue_S, HandleBranchTrue));
			handlers.Add(new OpcodeHandler(OpCodes.Brfalse, HandleBranchFalse));
			handlers.Add(new OpcodeHandler(OpCodes.Brfalse_S, HandleBranchFalse));
			handlers.Add(new OpcodeHandler(OpCodes.Br, HandleBranch));
			handlers.Add(new OpcodeHandler(OpCodes.Br_S, HandleBranchShort));
			handlers.Add(new OpcodeHandler(OpCodes.Beq, HandleBranchOnEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Beq_S, HandleBranchOnEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bne_Un, HandleBranchOnNotEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bne_Un_S, HandleBranchOnNotEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bge, HandleBranchOnGreaterOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bge_S, HandleBranchOnGreaterOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bge_Un, HandleBranchOnGreaterOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bge_Un_S, HandleBranchOnGreaterOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bgt, HandleBranchOnGreaterThan));
			handlers.Add(new OpcodeHandler(OpCodes.Bgt_S, HandleBranchOnGreaterThan));
			handlers.Add(new OpcodeHandler(OpCodes.Bgt_Un, HandleBranchOnGreaterOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Bgt_Un_S, HandleBranchOnGreaterOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Ble, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Ble_S, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Ble_Un, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Ble_Un_S, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Blt, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Blt_S, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Blt_Un, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Blt_Un_S, HandleBranchOnLessOrEqual));
			handlers.Add(new OpcodeHandler(OpCodes.Leave, HandleLeave));
			handlers.Add(new OpcodeHandler(OpCodes.Leave_S, HandleLeaveShort));
			handlers.Add(new OpcodeHandler(OpCodes.Switch, HandleSwitch));
			// Exceptions
			handlers.Add(new OpcodeHandler(OpCodes.Throw, HandleThrow));
			handlers.Add(new OpcodeHandler(OpCodes.Rethrow, HandleRethrow));
			handlers.Add(new OpcodeHandler(OpCodes.Endfinally, HandleNop));
			// Other
			handlers.Add(new OpcodeHandler(OpCodes.Sizeof, HandleSizeOf));

			OpcodeHandlerTable = handlers.ToDictionary(handler => handler.opcode.Value);
		}

		private static string GenerateVariableName(MsilDecompilationContext context, Variable variable, string nameHint)
		{
			char initialChar = (string.IsNullOrEmpty(nameHint) ? '\0' : nameHint[0]);
			if (char.IsLetter(initialChar))
			{
				if (char.IsUpper(initialChar))
				{
					nameHint = char.ToLower(initialChar) + nameHint.Substring(1);
				}
			}
			else if (initialChar != '_')
			{
				nameHint = "_" + nameHint;
			}

			bool theNameWorks;

			string name = nameHint;
			int i = 1;
			do
			{
				if (i > 1)
				{
					name = nameHint + i;
				}
				var nameMatcher = Symbol.GetNameMatcher(name);
				theNameWorks =
					context.LocalVariables.Find(nameMatcher) == null
					&& context.ParameterVariables.Find(nameMatcher) == null
					;
				i++;
			} while (!theNameWorks);

			return name;
		}
		private static bool IsVariableUnnamed(Variable variable)
		{
			return (variable.Name.StartsWith("$"));
		}
		private static string SuggestNameHint(Expression expression, Type type)
		{
			MemberExpression propertyReference = expression as MemberExpression;
			if (propertyReference != null)
			{
				return propertyReference.Member.Name;
			}
			else
			{
				BoundFunctionCallExpression functionCall = expression as BoundFunctionCallExpression;
				if (functionCall != null)
				{
					string functionName = functionCall.FunctionName;
					if (
						functionName.StartsWith("get", StringComparison.InvariantCultureIgnoreCase)
						&& functionName.Length > "get".Length
						)
						functionName = functionName.Substring(3);
					return functionName;
				}
				else
				{
					string nameHint;
					if (
						Equal(type, typeof(int))
						|| Equal(type, typeof(uint))
						|| Equal(type, typeof(decimal))
						|| Equal(type, typeof(double))
						|| Equal(type, typeof(float))
						|| Equal(type, typeof(short))
						|| Equal(type, typeof(ushort))
						|| Equal(type, typeof(sbyte))
						|| Equal(type, typeof(byte))
						|| Equal(type, typeof(long))
						|| Equal(type, typeof(ulong))
						)
					{
						nameHint = "num";
					}
					else if (Equal(type, typeof(string)))
					{
						nameHint = "text";
					}
					else if (Equal(type, typeof(bool)))
					{
						nameHint = "flag";
					}
					else if (Equal(type, typeof(object)))
					{
						nameHint = "obj";
					}
					else if (type.HasElementType)
					{
						string baseNameHint = SuggestNameHint(expression, type.GetElementType());
						if (type.IsArray) nameHint = baseNameHint + "Array";
						else if (type.IsPointer) nameHint = baseNameHint + "Pointer";
						else nameHint = baseNameHint;
					}
					else
					{
						nameHint = type.Name;
						if (type.IsInterface && nameHint.StartsWith("I"))
							nameHint = nameHint.Substring(1);
						if (nameHint.Contains('`')) nameHint = nameHint.Substring(0, nameHint.IndexOf('`'));
					}

					return nameHint;
				}
			}
		}

		private static void EnsureVariableNamed(MsilDecompilationContext context, Variable variable, Expression expression, Type type)
		{
			EnsureVariableNamed(context, variable, SuggestNameHint(expression, type));
		}
		private static void EnsureVariableNamed(MsilDecompilationContext context, Variable variable, string nameHint)
		{
			if (IsVariableUnnamed(variable))
			{
				variable.Name = GenerateVariableName(context, variable, nameHint);
			}
		}

		private static void HandleSizeOf(MsilDecompilationContext context, object operand)
		{
			Type type = (Type)operand;
			context.PushStackEntry(new SizeofExpression(new BoundTypeReference(type)), typeof(int));
		}
		private static void HandleNop(MsilDecompilationContext context, object operand)
		{
			// Do nothing, usually denotes a brace in debug code
		}
		private static void HandleLocAlloc(MsilDecompilationContext context, object operand)
		{
			context.PushStackEntry(new LocalAllocExpression(context.PopExpression()), typeof(void).MakePointerType());
		}
		private static void HandleArgList(MsilDecompilationContext context, object operand)
		{
			context.PushStackEntry(new ArgumentListExpression(), typeof(IntPtr));
		}
		private static void HandlePop(MsilDecompilationContext context, object operand)
		{
			Expression expression = context.PopExpression();
			ExpressionStatement statement = new ExpressionStatement(expression);
			context.RecordStatement(statement);
		}
		private static void HandleDuplicate(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry stackEntry = context.PopStackEntry();
			context.PushStackEntry(stackEntry);
			context.PushStackEntry(stackEntry);
		}

		private static void HandleReturn(MsilDecompilationContext context, object operand)
		{
			MethodInfo method = context.Method as MethodInfo;
			bool hasReturnValue = (method != null) && (method.ReturnType != typeof(void));
			Expression returnValue = null;
			if (hasReturnValue)
			{
				returnValue = context.PopExpression();
				returnValue = CoerceExpression(returnValue, ((MethodInfo)context.Method).ReturnType, context);
			}
			context.RecordStatement(new ReturnStatement(returnValue));
		}
		private static void HandleLoadConstantShort(MsilDecompilationContext context, object operand)
		{
			HandleLoadConstant(context, (int)(sbyte)operand);
		}
		private static void HandleLoadConstant(MsilDecompilationContext context, object operand)
		{
			Type type = ((operand == null) ? typeof(object) : operand.GetType());
			context.PushStackEntry(ConstantExpression.FromValue(operand), type);
		}
		private static void HandleLoadString(MsilDecompilationContext context, object operand)
		{
			string str = (string)operand;
			context.PushStackEntry(ConstantExpression.FromValue(str), typeof(string));
		}
		private static void HandleInitializeObject(MsilDecompilationContext context, object operand)
		{
			Type objectType = (Type)operand;
			UnboundNewObjectExpression newObject = new UnboundNewObjectExpression(new BoundTypeReference(objectType), new Expression[0]);

			Expression address = context.PopExpression();
			IndirectExpression indirect = (IndirectExpression)address;
			HandleAssignment(context, indirect.Expression, newObject, objectType);
		}
		private static void HandleAdd(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression + right, leftEntry.ExpressionType);
		}
		private static void HandleSubtract(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression - right, leftEntry.ExpressionType);
		}
		private static void HandleMultiply(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression * right, leftEntry.ExpressionType);
		}
		private static void HandleDivide(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression / right, leftEntry.ExpressionType);
		}
		private static void HandleRemainder(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression % right, leftEntry.ExpressionType);
		}
		private static void HandleNegation(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry operandEntry = context.PopStackEntry();
			context.PushStackEntry(-operandEntry.Expression, operandEntry.ExpressionType);
		}
		private static void HandleBitwiseAnd(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression & right, leftEntry.ExpressionType);
		}
		private static void HandleBitwiseOr(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression | right, leftEntry.ExpressionType);
		}
		private static void HandleBitwiseExclusiveOr(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			context.PushStackEntry(leftEntry.Expression ^ right, leftEntry.ExpressionType);
		}
		private static void HandleShiftLeft(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			context.PushStackEntry(Expression.LeftShift(leftEntry.Expression, rightEntry.Expression), leftEntry.ExpressionType);
		}
		private static void HandleShiftRight(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			context.PushStackEntry(Expression.RightShift(leftEntry.Expression, rightEntry.Expression), leftEntry.ExpressionType);
		}
		private static void HandleBitwiseComplement(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry valueEntry = context.PopStackEntry();
			context.PushStackEntry(~valueEntry.Expression, valueEntry.ExpressionType);
		}
		private static void HandleCheckEquality(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();

			if (
				(Equal(leftEntry.ExpressionType, typeof(bool)))
				&& (rightEntry.Expression == ConstantExpression.ZeroInt)
				)
			{
				context.PushStackEntry(!leftEntry.Expression, typeof(bool));
			}
			else
			{
				Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
				context.PushStackEntry(Expression.Equality(leftEntry.Expression, right), typeof(bool));
			}
		}
		private static void HandleCheckGreaterThan(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			context.PushStackEntry(leftEntry.Expression > rightEntry.Expression, typeof(bool));
		}
		private static void HandleCheckLessThan(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			context.PushStackEntry(leftEntry.Expression < rightEntry.Expression, typeof(bool));
		}
		private static void HandleNewObject(MsilDecompilationContext context, object operand)
		{
			ConstructorInfo constructor = (ConstructorInfo)operand;

			Expression[] arguments = GetParameterExpressions(context, constructor.GetParameters());

			if (
				constructor.ReflectedType.IsSubclassOf(typeof(Delegate))
				&& (arguments.Length == 2)
				)
			{
				FunctionPointerExpression pointer = (FunctionPointerExpression)arguments[1];
				MethodReferenceExpression methodRef = new MethodReferenceExpression(arguments[0], pointer.Method);
				NewObjectExpression newDelegateExpression = new BoundConstructorExpression(constructor, new Expression[] { methodRef });
				context.PushStackEntry(newDelegateExpression, constructor.ReflectedType);
			}
			else if (constructor.ReflectedType.IsArray)
			{
				NewArrayExpression newArray = new NewArrayExpression(constructor.ReflectedType.GetElementType(), arguments);
				context.PushStackEntry(newArray, constructor.ReflectedType);
			}
			else
			{
				BoundConstructorExpression newObj = new BoundConstructorExpression(constructor, arguments);
				context.PushStackEntry(newObj, constructor.ReflectedType);
			}
		}
		private static Expression[] GetParameterExpressions(
			MsilDecompilationContext context,
			ParameterInfo[] parameters
			)
		{
			int parameterCount = parameters.Length;

			Expression[] arguments = new Expression[parameterCount];
			for (int parameterIndex = parameterCount - 1; parameterIndex >= 0; parameterIndex--)
			{
				ParameterInfo parameter = parameters[parameterIndex];
				Expression argument = context.PopExpression();

				Type argumentType = parameter.ParameterType;
				argument = CoerceExpression(argument, argumentType, context);
				arguments[parameterIndex] = argument;

				BoundVariableReferenceExpression variableRef = argument as BoundVariableReferenceExpression;
				if (
					(parameter != null)
					&& (variableRef != null)
					&& (variableRef.Variable is LocalVariable)
					)
				{
					EnsureVariableNamed(context, variableRef.Variable, parameter.Name);
				}
			}

			return arguments;
		}
		private static void HandleCall(MsilDecompilationContext context, object operand)
		{
			MethodBase methodBase = (MethodBase)operand;
			{
				ConstructorInfo constructor = methodBase as ConstructorInfo;
				if (constructor != null)
				{
					HandleNewObject(context, operand);
					Expression newObject = context.PopExpression();
					Expression variableRef = context.PopExpression();
					IndirectExpression indirect = variableRef as IndirectExpression;
					if (indirect != null)
						variableRef = indirect.Expression;
					HandleAssignment(context, variableRef, newObject);
					return;
				}
			}
			MethodInfo method = (MethodInfo)methodBase;

			ParameterInfo[] parameters = method.GetParameters();

			Expression instanceArgument = null;
			Type instanceType = null;
			Expression[] arguments = GetParameterExpressions(context, parameters);
			if (!method.IsStatic)
			{
				MsilStackEntry instanceStackEntry = context.PopStackEntry();
				instanceType = instanceStackEntry.ExpressionType;
				instanceArgument = instanceStackEntry.Expression;
				if (method.ReflectedType.IsInterface && !instanceStackEntry.ExpressionType.IsInterface)
				{
					// Account for interface calls on value types
					if (instanceArgument is IndirectExpression)
						instanceArgument = ((IndirectExpression)instanceArgument).Expression;
					instanceArgument = new CastExpression(instanceArgument, method.ReflectedType);
				}
				else if (method.ReflectedType.IsValueType)
				{
					IndirectExpression indirect = instanceArgument as IndirectExpression;
					if (indirect != null)
					{
						instanceArgument = indirect.Expression;
					}
				}
				else
				{
					instanceArgument = CoerceExpression(instanceArgument, method.ReflectedType, context);
				}
			}

			if (
				(Equal(method, ((Func<RuntimeTypeHandle, Type>)Type.GetTypeFromHandle).Method))
				&& (arguments[0] is MetadataTokenExpression)
				)
			{
				TypeReferenceExpression typeRef = new TypeReferenceExpression((Type)((MetadataTokenExpression)arguments[0]).Member);
				context.PushStackEntry(typeRef, typeof(Type));
			}
			else
			{
				if (method.IsStatic && Equal(method.ReflectedType, typeof(Queryable)))
				{
					Expression queryExpression = LinqConverter.TryRaiseLinqQuery(method, arguments);
					if (queryExpression != null)
					{
						context.PushStackEntry(queryExpression, method.ReturnType);
						return;
					}
				}

				PropertyInfo property = null;
				bool isSetter = false;
				bool isGetter = false;
				bool isEventAttach = false;
				bool isEventDetach = false;
				bool isStringConcat = Equal(method, ((Func<string, string, string>)string.Concat).Method);
				bool isEnumHasFlag = method.Name == "HasFlag" && Equal(method.DeclaringType, typeof(Enum));
				bool isCollectionAdd =
					(method.Name == "Add")
					&& (instanceArgument != null)
					&& (
						(instanceArgument is NewObjectExpression)
						|| (instanceArgument is NewImplicitCollectionExpression)
						);

				if (isEnumHasFlag)
				{
					arguments[0] = CoerceExpression(arguments[0], instanceType, context);
				}

				bool isIndexerSetter = ((arguments.Length > 0) && (method.Name == "set_Item")) || (method.Name == "Set" && method.ReflectedType.IsArray);
				bool isIndexerGetter = ((arguments.Length > 0) && (method.Name == "get_Item")) || (method.Name == "Get" && method.ReflectedType.IsArray);
				bool isArrayElementReference =
					(method.Name == "Address")
					&& (method.ReflectedType.IsArray)
					;

				if (
					!(isIndexerGetter || isIndexerSetter)
					&& method.IsPropertyAccessor()
					)
				{
					string propertyName = method.Name.Substring(4);
					property = method.Property();

					// HACK: C# does not support indexed properties
					if ((property != null) && (property.GetIndexParameters().Length == 0))
					{
						isSetter = method == property.GetSetMethod(true);
						isGetter = method == property.GetGetMethod(true);
					}
				}
				else if (
					method.IsEventAccessor()
					)
				{
					isEventAttach = method.Name.StartsWith("add_");
					isEventDetach = method.Name.StartsWith("remove_");
				}

				bool isBinaryOperator = arguments.Length == 2 && CSharpOperatorOverloads.IsBinaryOperatorOverload(method);
				bool isUnaryOperator = arguments.Length == 1 && CSharpOperatorOverloads.IsUnaryOperatorOverload(method);

				if (isIndexerGetter || isIndexerSetter)
				{
					if (isIndexerGetter)
					{
						IndexerExpression indexer = new IndexerExpression(instanceArgument, arguments);
						context.PushStackEntry(indexer, method.ReturnType);
					}
					else
					{
						var indexes = arguments.Take(arguments.Length - 1).ToArray();
						IndexerExpression indexer = new IndexerExpression(instanceArgument, indexes);
						var value = arguments.Last();
						HandleAssignment(context, indexer, value);
					}
				}
				else if (isGetter || isSetter)
				{
					if (
						(method.DeclaringType != null)
						&& method.DeclaringType.IsValueType
						)
					{
						IndirectExpression indirect = instanceArgument as IndirectExpression;
						if (indirect != null)
							instanceArgument = indirect.Expression;
					}

					PropertyReferenceExpression propertyRef = new PropertyReferenceExpression(instanceArgument, property);

					if (isGetter) context.PushStackEntry(propertyRef, method.ReturnType);
					else if (isSetter)
					{
						Expression newValue = arguments.Last();
						if (!TryAppendMemberInitializer(context, instanceArgument, new MemberInitializer(property, newValue)))
						{
							HandleAssignment(context, propertyRef, newValue, property.PropertyType);
						}
					}
				}
				else if (isEventAttach || isEventDetach)
				{
					EventInfo @event = method.Event();

					EventHandlerOperation operation;
					if (method == @event.GetAddMethod()) operation = EventHandlerOperation.Attach;
					else if (method == @event.GetRemoveMethod()) operation = EventHandlerOperation.Detach;
					else if (method == @event.GetRaiseMethod()) operation = EventHandlerOperation.Raise;
					else operation = EventHandlerOperation.Other;

					EventHandlerStatement handlerStatement = new EventHandlerStatement(instanceArgument, @event, arguments[0], operation);
					context.RecordStatement(handlerStatement);
				}
				else if (isUnaryOperator)
				{
					Expression overloadExpression = CSharpOperatorOverloads.GetExpression(method, arguments[0]);
					context.PushStackEntry(CSharpOperatorOverloads.GetExpression(method, arguments[0]), method.ReturnType);
				}
				else if (isBinaryOperator)
				{
					Expression overloadExpression = CSharpOperatorOverloads.GetExpression(method, arguments[0], arguments[1]);
					context.PushStackEntry(overloadExpression, method.ReturnType);
				}
				else if (isStringConcat && (arguments.Length == 2))
				{
					Expression concatExpression = new BinaryOperationExpression(arguments[0], arguments[1], ExpressionOperator.Addition, method);
					context.PushStackEntry(concatExpression, method.ReturnType);
				}
				else if (isArrayElementReference)
				{
					IndexerExpression indexer = new IndexerExpression(
						instanceArgument,
						arguments
						);
					context.PushStackEntry(indexer, method.ReflectedType.GetElementType());
				}
				else if (isCollectionAdd)
				{
					NewObjectExpression newObject = instanceArgument as NewObjectExpression;
					NewImplicitCollectionExpression implicitCollection = null;
					if (newObject != null)
					{
						implicitCollection = new NewImplicitCollectionExpression(newObject, arguments, method);
					}
					else
					{
						implicitCollection = instanceArgument as NewImplicitCollectionExpression;
						if (implicitCollection != null)
						{
							implicitCollection = new NewImplicitCollectionExpression(
								implicitCollection.NewObjectExpression,
								implicitCollection.Subexpressions.Concat(arguments),
								method
								);
						}
					}

					// Pop previous "new"
					MsilStackEntry previousStackEntry = context.PopStackEntry();
					context.PushStackEntry(implicitCollection, previousStackEntry.ExpressionType);
				}
				else
				{
					MethodCallFunction function = new BoundMethodCallFunction(method.Name, method, instanceArgument, 0);
					FunctionCallExpression call = new BoundFunctionCallExpression(function, arguments.ToArray());

					if (Equal(method.ReturnType, typeof(void)))
					{
						bool isSpecial = false;
						if
							(Equal(method, ((Action<Array, RuntimeFieldHandle>)System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray).Method))
						{
							if (context.ExpressionCount > 0)
							{
								NewVectorExpression newArrayExpression = context.PeekExpression() as NewVectorExpression;
								if (newArrayExpression == arguments[0])
								{
									ConstantExpression arrayLengthConstantExpression = newArrayExpression.LengthExpression as ConstantExpression;
									MetadataTokenExpression tokenExpression = arguments[1] as MetadataTokenExpression;

									if (
										(tokenExpression != null)
										&& (arrayLengthConstantExpression != null)
										)
									{
										int arrayLength = Convert.ToInt32(arrayLengthConstantExpression.Value);
										FieldInfo field = (FieldInfo)tokenExpression.Member;
										Type elementType = newArrayExpression.ElementType;
										Expression initializedArrayExpression = TryBuildInitializedArrayExpression(
											field,
											arrayLength,
											elementType
											);

										if (initializedArrayExpression != null)
										{
											context.PopExpression();	// Pop new empty array
											context.PushStackEntry(initializedArrayExpression, elementType.MakeArrayType());

											isSpecial = true;
										}
									}

									//object arrayValue = arrayInitializerField.GetValue(null);
									//Type arrayValueType = arrayValue.GetType();
									// Load array value from module
								}
							}
						}

						if (!isSpecial)
						{
							context.RecordStatement(new ExpressionStatement(call));
						}
					}
					else
					{
						context.PushStackEntry(call, method.ReturnType);
					}
				}
			}
		}

		private static Expression TryBuildInitializedArrayExpression(
			FieldInfo field,
			int arrayLength,
			Type elementType)
		{
			if (field == null) throw new ArgumentNullException("field");
			if (elementType == null) throw new ArgumentNullException("elementType");

			NewImplicitArrayExpression arrayExpression = null;
			IExtendedFieldInfo extendedField = field as IExtendedFieldInfo;
			if (extendedField != null)
			{
				byte[] fieldData = extendedField.GetRawData();
				if (fieldData != null)
				{
					MemoryStream stream = new MemoryStream(fieldData);
					BinaryReader reader = new BinaryReader(stream);

					List<Expression> elements = new List<Expression>(arrayLength);
					TypeCode elementTypeCode = Type.GetTypeCode(elementType);
					for (int i = 0; (i < arrayLength) && (reader.PeekChar() != -1); i++)
					{
						object value = null;
						switch (elementTypeCode)
						{
							case TypeCode.Byte:
								value = reader.ReadByte();
								break;
							case TypeCode.SByte:
								value = reader.ReadSByte();
								break;
							case TypeCode.Int16:
								value = reader.ReadInt16();
								break;
							case TypeCode.UInt16:
								value = reader.ReadUInt16();
								break;
							case TypeCode.Int32:
								value = reader.ReadInt32();
								break;
							case TypeCode.UInt32:
								value = reader.ReadUInt32();
								break;
							case TypeCode.Int64:
								value = reader.ReadInt64();
								break;
							case TypeCode.UInt64:
								value = reader.ReadUInt64();
								break;
						}

						Expression expression = ConstantExpression.FromValue(value);
						elements.Add(expression);
					}

					arrayExpression = new NewImplicitArrayExpression(elementType, elements);
				}
			}

			return arrayExpression;
		}

		private static void HandleCallIndirect(MsilDecompilationContext context, object operand)
		{
			Expression target = context.PopExpression();

			MethodRefSignature methodRefSignature = SignatureReader.ReadMethodRefSignature((byte[])operand);
			Expression[] arguments = new Expression[methodRefSignature.parameters.Length];
			Type[] genericTypeArgs = Type.EmptyTypes;
			if (context.Method.DeclaringType != null)
				genericTypeArgs = context.Method.DeclaringType.GetGenericArguments();
			Type[] genericMethodArgs = context.Method.GetGenericArguments();
			for (int i = arguments.Length - 1; i >= 0; i--)
			{
				Param param = methodRefSignature.parameters[i];
				Expression argument = context.PopExpression();
				Type parameterType = param.ResolveType(
					context.Method.Module,
					true,
					genericTypeArgs,
					genericMethodArgs
					);
				argument = CoerceExpression(argument, parameterType, context);
				arguments[i] = argument;
			}

			IndirectCallExpression call = new IndirectCallExpression(target, arguments);
			Type returnType = methodRefSignature.returnType.ResolveType(
				context.Method.Module,
				true,
				genericTypeArgs,
				genericMethodArgs
				);
			if (methodRefSignature.HasReturnValue())
				context.PushStackEntry(call, returnType);
			else
				context.RecordStatement(new ExpressionStatement(call));
		}
		//private static TypedVariable TryResolveLambdaParameter(Expression expression, MsilDisassemblyContext context)
		//{
		//    TypedVariable parameterVariable = null;

		//    BoundVariableReferenceExpression variableRef = expression as BoundVariableReferenceExpression;
		//    if (variableRef != null)
		//    {
		//        context.LinqParameterTable.TryGetValue(variableRef.Variable, out parameterVariable);
		//    }
		//    else
		//    {
		//        AssignmentExpression assignment = expression as AssignmentExpression;
		//        if (assignment != null)
		//        {
		//            parameterVariable = TryResolveLambdaParameter(assignment.Left, context);
		//        }
		//    }

		//    return parameterVariable;
		//}

		private static TypedVariable TryRaiseLambdaParameter(Expression expression)
		{
			ObjectTemplate<TypeReferenceExpression> typeRefParam;
			ObjectTemplate<StringConstantExpression> nameParam;
			ObjectTemplate<BoundFunctionCallExpression> functionCallCemplate = new ObjectTemplate<BoundFunctionCallExpression>(
				new MemberTemplate[] {
					new PropertyTemplate<BoundMethodCallFunction>(
						"Function",
						new ObjectTemplate<BoundMethodCallFunction>(
							new MemberTemplate[] {
								new PropertyTemplate<MethodInfo>("Method", new NodeEqualityTemplate<MethodInfo>(((Func<Type, string, LinqExpressions.ParameterExpression>)LinqExpression.Parameter).Method, MemberEqualityComparer.Instance))
							})
						),
					new PropertyTemplate<Expression[]>(
						"Arguments",
						new ArrayTemplate<Expression>(
							new ElementTemplate[] {
								new ElementTemplate<TypeReferenceExpression>(
									0,
									typeRefParam=new ObjectTemplate<TypeReferenceExpression>()
									),
								new ElementTemplate<StringConstantExpression>(
									1,
									nameParam=new ObjectTemplate<StringConstantExpression>()
									)
							})
						)
				});

			bool matches = functionCallCemplate.Inspect(expression);
			if (!matches) return null;

			Type paramType = typeRefParam.CapturedValue.Type as Type;
			string paramName = nameParam.CapturedValue.Value as string;

			TypedVariable parameterVariable = new WorkspaceVariable(paramName, paramType);
			return parameterVariable;
		}

		private static void HandleLoadFunction(MsilDecompilationContext context, object operand)
		{
			MethodInfo method = (MethodInfo)operand;
			context.PushStackEntry(new FunctionPointerExpression(method), typeof(IntPtr));
		}
		private static void HandleLoadVirtualFunction(MsilDecompilationContext context, object operand)
		{
			MethodInfo method = (MethodInfo)operand;
			Expression instance = context.PopExpression();
			context.PushStackEntry(new FunctionPointerExpression(method), typeof(IntPtr));
		}
		private static void HandleNewArray(MsilDecompilationContext context, object operand)
		{
			Expression lengthExpression = context.PopExpression();
			Type elementType = (Type)operand;
			NewArrayExpression arrayExpression = new NewVectorExpression(elementType, lengthExpression);
			context.PushStackEntry(arrayExpression, elementType.MakeArrayType());
		}
		private static void HandleLoadArrayLength(MsilDecompilationContext context, object operand)
		{
			Expression array = context.PopExpression();
			PropertyInfo lengthProperty = typeof(Array).GetProperty("Length");
			PropertyReferenceExpression lengthRefExpression = new PropertyReferenceExpression(array, lengthProperty);
			context.PushStackEntry(lengthRefExpression, typeof(UIntPtr));
		}
		private static void HandleLoadElement(MsilDecompilationContext context, object operand)
		{
			Expression index = context.PopExpression();
			MsilStackEntry arrayEntry = context.PopStackEntry();
			IndexerExpression indexer = new IndexerExpression(arrayEntry.Expression, new Expression[] { index });
			HandleLoadValue(context, indexer, arrayEntry.ExpressionType.GetElementType());
		}
		private static void HandleLoadElementAddress(MsilDecompilationContext context, object operand)
		{
			Expression index = context.PopExpression();
			MsilStackEntry arrayEntry = context.PopStackEntry();
			IndexerExpression indexer = new IndexerExpression(arrayEntry.Expression, new Expression[] { index });
			context.PushStackEntry(new IndirectExpression(indexer), arrayEntry.ExpressionType.MakeByRefType());
		}

		private static bool TryAppendArrayElement(MsilDecompilationContext context, Expression array, Expression indexExpression, Expression value)
		{
			AssignmentExpression assignment = array as AssignmentExpression;
			if (assignment == null) return false;

			ISettableExpression settableBase = (ISettableExpression)assignment.Left;

			IntegerConstantExpression intIndex = indexExpression as IntegerConstantExpression;
			if (intIndex == null) return false;

			int index = intIndex.TypedValue;

			Expression ghosted = assignment.Right;
			if (ghosted == null) return false;

			NewArrayExpression newArray = ghosted as NewArrayExpression;
			if (
				(newArray != null)
				&& (index == 0)
				)
			{
				value = CoerceExpression(value, newArray.ElementType, context);
				NewImplicitArrayExpression implicitArray = new NewImplicitArrayExpression(newArray.ElementType, new Expression[] { value });
				context.GhostAssign(settableBase, implicitArray);
				return true;
			}
			else
			{
				NewImplicitArrayExpression implicitArray = ghosted as NewImplicitArrayExpression;
				if (
					(implicitArray != null)
					&& (index == implicitArray.Arguments.Count())
					)
				{
					value = CoerceExpression(value, implicitArray.ElementType, context);
					List<Expression> elements = new List<Expression>(implicitArray.Arguments);
					elements.Add(value);
					implicitArray = new NewImplicitArrayExpression(implicitArray.ElementType, elements);
					context.GhostAssign(settableBase, implicitArray);
					return true;
				}
				else
				{
					context.GhostAssign(settableBase, ghosted);
					return false;
				}
			}
		}
		private static void HandleStoreElement(MsilDecompilationContext context, object operand)
		{
			Expression rvalue = context.PopExpression();
			Expression index = context.PopExpression();
			MsilStackEntry arrayEntry = context.PopStackEntry();
			if (!TryAppendArrayElement(context, arrayEntry.Expression, index, rvalue))
			{
				IndexerExpression lvalue = new IndexerExpression(arrayEntry.Expression, new Expression[] { index });
				HandleAssignment(context, lvalue, rvalue, arrayEntry.ExpressionType.GetElementType());
			}
		}
		private static void HandleAssignment(MsilDecompilationContext context, Expression settable, Expression value, Type type = null)
		{
			if (type != null)
			{
				value = CoerceExpression(value, type, context);
			}

			BoundVariableReferenceExpression boundVariable = settable as BoundVariableReferenceExpression;
			bool allowGhosting = false;
			if (boundVariable != null)
			{
				MsilVariableReference nextReference = context.FindNextVariableReference(context.CurrentOffset, boundVariable.Variable);
				if ((nextReference != null) && (nextReference.Type == MsilVariableReferenceType.Load))
				{
					nextReference = context.FindNextVariableReference(nextReference.Offset, boundVariable.Variable);
					if ((nextReference == null) || (nextReference.Type == MsilVariableReferenceType.Store))
						allowGhosting = true;
				}
			}

			bool preferGhosting =
				(allowGhosting)
				&& (
					(value is NewObjectExpression)
					|| (value is NewArrayExpression)
					|| (value is NewImplicitArrayExpression)
					|| (context.Statements == null)
				);


			if (preferGhosting)
			{
				context.GhostAssign((ISettableExpression)settable, value);
			}
			else
			{
				CreateDeclareOrAssignStatement(context, settable, value);
			}

			// UNDONE: This causes a problem with switched expressions, where the true block may contain one assignment statement
			//if (context.ExpressionCount != 0)
			//{
			//    context.GhostAssign(settable, value);
			//}
			//else
			//{
			//    Statement statement = new AssignmentStatement(settable, value);
			//    context.RecordStatement(statement);
			//}
		}
		private static void HandleLoadValue(MsilDecompilationContext context, Expression settable, Type valueType)
		{
			Expression value = null;
			if (settable is ISettableExpression)
				value = context.UnghostValue((ISettableExpression)settable);

			if (value != null)
			{
				bool isTemporary = false;

				BoundVariableReferenceExpression variableRef = settable as BoundVariableReferenceExpression;
				LocalVariable localVariable = null;
				if (variableRef != null)
				{
					localVariable = variableRef.Variable as LocalVariable;
					if (localVariable != null)
					{
						MsilVariableReference nextReference = context.FindNextVariableReference(context.CurrentInstruction.NextOffset, variableRef.Variable);
						bool isVariableLoadedAgain =
							(
								(nextReference != null)
								&& (nextReference.Type != MsilVariableReferenceType.Store)
								)
							;
						if (!isVariableLoadedAgain)
						{
							// Check for loops
							IEnumerable<MsilBranch> loopBranches = context.FindLoopBranches(context.CurrentInstruction.NextOffset, context.CurrentOffset);
							foreach (var loopBranch in loopBranches)
							{
								nextReference = context.FindNextVariableReference(loopBranch.TargetOffset, variableRef.Variable);
								isVariableLoadedAgain =
									(
										(nextReference != null)
										&& (nextReference.Type != MsilVariableReferenceType.Store)
										)
									;
								if (isVariableLoadedAgain) break;
							}
						}

						isTemporary = !isVariableLoadedAgain;

						if (!isTemporary)
						{
							context.StatementLocalVariableReferences.Add(localVariable);
						}
					}
				}

				if (!isTemporary)
				{
					// UNDONE: Prevents implicit-sized (initialized) arrays
					//if ((context.ExpressionCount == 0) || true)	// HACK: Prevent inline assignment
					if ((context.ExpressionCount == 0))
					{
						CreateDeclareOrAssignStatement(context, settable, value);
						value = (Expression)settable;
					}
					else
					{
						AssignmentExpression assignment = new AssignmentExpression(settable, value);
						value = assignment;
					}
				}
			}
			else
			{
				value = (Expression)settable;
			}

			context.PushStackEntry(value, valueType);
		}

		private static void CreateDeclareOrAssignStatement(MsilDecompilationContext context, Expression settable, Expression value)
		{
			BoundVariableReferenceExpression variableRef = settable as BoundVariableReferenceExpression;
			LocalVariable localVariable = (variableRef != null) ? variableRef.Variable as LocalVariable : null;
			if (
				(localVariable != null)
				&& (!context.DeclaredVariables.Contains(localVariable))
				)
			{
				VariableDeclarationStatement assignment = new VariableDeclarationStatement(localVariable) { Initializer = value };
				context.DeclaredVariables.Add(localVariable);
				context.RecordStatement(assignment);
			}
			else
			{
				AssignmentStatement assignment = new AssignmentStatement(settable, value);
				context.RecordStatement(assignment);
			}
		}

		private static void HandleLoadLocalShort(MsilDecompilationContext context, object operand)
		{
			HandleLoadLocal(context, (int)(byte)operand);
		}
		private static void HandleLoadLocal(MsilDecompilationContext context, object operand)
		{
			int localIndex = (int)operand;
			LocalVariable variable = context.LocalVariables[localIndex];

			if (
				(variable.Usage == LocalVariableUsage.Decision)
				)
			{
				context.PushStackEntry(context.GetDecision(variable), variable.StaticVariableType);
			}
			else
			{
				BoundVariableReferenceExpression variableRef = new BoundVariableReferenceExpression(variable);
				HandleLoadValue(context, variableRef, variable.StaticVariableType);
			}
		}
		private static void HandleLoadLocalAddressShort(MsilDecompilationContext context, object operand)
		{
			HandleLoadLocalAddress(context, (int)(byte)operand);
		}
		private static void HandleLoadLocalAddress(MsilDecompilationContext context, object operand)
		{
			int localIndex = (int)operand;
			LocalVariable variable = context.LocalVariables[localIndex];
			BoundVariableReferenceExpression variableRef = new BoundVariableReferenceExpression(variable);

			// HACK: MakePointerType and MakeRefType fail for TypedReference
			Type refType =
				(Equal(variable.StaticVariableType, typeof(TypedReference)))
				? typeof(void).MakePointerType()
				: variable.StaticVariableType.MakeByRefType()
				;
			context.PushStackEntry(new IndirectExpression(variableRef), refType);
		}
		private static void HandleStoreLocalShort(MsilDecompilationContext context, object operand)
		{
			HandleStoreLocal(context, (int)(byte)operand);
		}
		private static void HandleStoreLocal(MsilDecompilationContext context, object operand)
		{
			int localIndex = (int)operand;
			LocalVariable variable = context.LocalVariables[localIndex];
			//context.RecordVariableReference(variable, MsilVariableReferenceType.Store);

			Expression value = context.PopExpression();
			value = CoerceExpression(value, variable.LocalInfo.LocalType, context);

			if (variable.Usage == LocalVariableUsage.Decision)
			{
				context.RecordDecision(variable, value);
			}
			else
			{
				if (Equal(variable.StaticVariableType, typeof(LinqExpressions.ParameterExpression)))
				{
					TypedVariable parameterVariable = TryRaiseLambdaParameter(value);
					context.LinqParameterTable[variable] = parameterVariable;
				}

				//context.StatementLocalVariableReferences.Add(variable);
				EnsureVariableNamed(context, variable, value, variable.LocalInfo.LocalType);

				//if (!context.DeclaredVariables.Contains(variable) && (context.ExpressionCount == 0))
				//{
				//    //if (context.ExpressionCount == 0)
				//    //{
				//    //    Statement assignment;
				//    //    {
				//    //        assignment = new AssignmentStatement(new BoundVariableReferenceExpression(variable), value);
				//    //    }
				//    //    else
				//    //    {
				//    Statement assignment = new VariableDeclarationStatement(variable) { Initializer = value };
				//    context.DeclaredVariables.Add(variable);
				//    context.RecordStatement(assignment);
				//}
				//else
				{
					VariableReferenceExpression variableRef = new BoundVariableReferenceExpression(variable);
					HandleAssignment(context, variableRef, value, variable.LocalInfo.LocalType);


					{
						PropertyReferenceExpression propertyRef = value as PropertyReferenceExpression;
						if (
							(propertyRef != null)
							&& (propertyRef.Property.Name == "Current")
							)
						{
							context.FlushGhosts();
						}
					}
				}
			}
		}
		private static void HandleLoadArgumentShort(MsilDecompilationContext context, object operand)
		{
			HandleLoadArgument(context, (int)(byte)operand);
		}
		private static void HandleLoadArgument(MsilDecompilationContext context, object operand)
		{
			int parameterIndex = (int)operand;

			if (
				(parameterIndex == 0)
				&& (!context.Method.IsStatic)
				)
			{
				if (context.Method.DeclaringType.IsValueType)
					context.PushStackEntry(new IndirectExpression(InstanceReferenceExpression.Instance), context.Method.ReflectedType.MakeByRefType());
				else
					context.PushStackEntry(InstanceReferenceExpression.Instance, context.Method.ReflectedType);
			}
			else
			{
				if (!context.Method.IsStatic) parameterIndex--;

				ParameterInfo parameter = context.Parameters[parameterIndex];
				Variable variable = context.ParameterVariables[parameterIndex];
				Expression expression = new BoundVariableReferenceExpression(variable);
				if (parameter.IsOut)
					// HACK: Won't work with generic parameters
					//HandleLoadValue(context, new IndirectExpression(expression), variable.VariableType.MakeByRefType());
					HandleLoadValue(context, new IndirectExpression(expression), typeof(void*));
				else
					HandleLoadValue(context, expression, variable.StaticVariableType);
			}
		}
		private static void HandleLoadArgumentAddressShort(MsilDecompilationContext context, object operand)
		{
			HandleLoadArgumentAddress(context, (int)(byte)operand);
		}
		private static void HandleLoadArgumentAddress(MsilDecompilationContext context, object operand)
		{
			int parameterIndex = (int)operand;

			if (!context.Method.IsStatic) parameterIndex--;

			Variable variable = context.ParameterVariables[parameterIndex];
			VariableReferenceExpression expression = new BoundVariableReferenceExpression(variable);
			context.PushStackEntry(new IndirectExpression(expression), variable.StaticVariableType.MakeByRefType());
		}
		private static void HandleStoreArgumentShort(MsilDecompilationContext context, object operand)
		{
			HandleStoreArgument(context, (int)(byte)operand);
		}
		private static void HandleStoreArgument(MsilDecompilationContext context, object operand)
		{
			int parameterIndex = (int)operand;
			if (!context.Method.IsStatic)
				parameterIndex--;
			MethodArgumentVariable variable = context.ParameterVariables[parameterIndex];

			Expression right = context.PopExpression();
			HandleAssignment(context, new BoundVariableReferenceExpression(variable), right, variable.Parameter.ParameterType);
		}
		private static void HandleLoadIndirectIntPtr(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(IntPtr));
		}
		private static void HandleLoadIndirectSByte(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(sbyte));
		}
		private static void HandleLoadIndirectInt16(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(short));
		}
		private static void HandleLoadIndirectInt(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(int));
		}
		private static void HandleLoadIndirectLong(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(long));
		}
		private static void HandleLoadIndirectSingle(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(float));
		}
		private static void HandleLoadIndirectDouble(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(double));
		}
		private static void HandleLoadIndirectByte(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(byte));
		}
		private static void HandleLoadIndirectUInt16(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(ushort));
		}
		private static void HandleLoadIndirectUInt(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(uint));
		}
		private static void HandleLoadIndirectRef(MsilDecompilationContext context, object operand)
		{
			HandleLoadObject(context, typeof(object));
		}
		private static void HandleLoadObject(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry valueEntry = context.PopStackEntry();
			IndirectExpression indirect = valueEntry.Expression as IndirectExpression;
			Expression value =
				(indirect != null)
				? indirect.Expression
				: new DereferenceExpression(valueEntry.Expression)
				;
			context.PushStackEntry(value, (Type)operand);
		}
		private static void HandleStoreIndirect(MsilDecompilationContext context, object operand)
		{
			Expression value = context.PopExpression();
			Expression address = context.PopExpression();
			IndirectExpression indirect = address as IndirectExpression;
			address =
				(indirect != null)
				? indirect.Expression
				: new DereferenceExpression(address)
				;

			HandleAssignment(context, address, value);
		}
		private static void HandleLoadField(MsilDecompilationContext context, object operand)
		{
			FieldInfo field = (FieldInfo)operand;

			Expression baseExpression =
				(field.IsStatic)
				? null
				: context.PopExpression()
				;
			if (!field.IsStatic && (field.DeclaringType != null) && field.DeclaringType.IsValueType)
			{
				IndirectExpression indirect = baseExpression as IndirectExpression;
				if (indirect != null)
					baseExpression = indirect.Expression;
			}

			Expression anonymousDelegateExpression;
			if (context.AnonymousMethodDelegateTable.TryGetValue(field, out anonymousDelegateExpression))
			{
				context.PushStackEntry(anonymousDelegateExpression, field.FieldType);
			}
			else
			{
				FieldReferenceExpression fieldReference = new FieldReferenceExpression(baseExpression, field);
				HandleLoadValue(context, fieldReference, field.FieldType);
			}
		}
		private static void HandleLoadFieldAddress(MsilDecompilationContext context, object operand)
		{
			FieldInfo field = (FieldInfo)operand;
			Expression baseExpression =
				(field.IsStatic)
				? null
				: context.PopExpression()
				;
			if (!field.IsStatic && (field.DeclaringType != null) && field.DeclaringType.IsValueType)
			{
				IndirectExpression indirect = baseExpression as IndirectExpression;
				if (indirect != null)
					baseExpression = indirect.Expression;
			}

			FieldReferenceExpression fieldReference = new FieldReferenceExpression(baseExpression, field);
			context.PushStackEntry(new IndirectExpression(fieldReference), field.FieldType.MakeByRefType());
		}
		private static void HandleLoadStaticField(MsilDecompilationContext context, object operand)
		{
			FieldInfo field = (FieldInfo)operand;
			FieldReferenceExpression fieldReference = new FieldReferenceExpression(null, field);

			Expression anonymousDelegateExpression;
			if (context.AnonymousMethodDelegateTable.TryGetValue(field, out anonymousDelegateExpression))
			{
				context.PushStackEntry(anonymousDelegateExpression, field.FieldType);
			}
			else
			{
				context.PushStackEntry(fieldReference, field.FieldType);
			}
		}
		private static void HandleLoadStaticFieldAddress(MsilDecompilationContext context, object operand)
		{
			FieldInfo field = (FieldInfo)operand;
			FieldReferenceExpression fieldReference = new FieldReferenceExpression(null, field);

			context.PushStackEntry(new IndirectExpression(fieldReference), field.FieldType.MakeByRefType());
		}
		private static bool TryAppendMemberInitializer(MsilDecompilationContext context, Expression baseExpression, MemberInitializer initializer)
		{
			AssignmentExpression assignment = baseExpression as AssignmentExpression;
			if (assignment == null) return false;

			ISettableExpression settableBase = (ISettableExpression)assignment.Left;

			Expression ghosted = assignment.Right;
			//if (ghosted == null) return false;

			BoundConstructorExpression newObject = ghosted as BoundConstructorExpression;
			if (newObject != null)
			{
				ObjectInitializerExpression initializedObject = new ObjectInitializerExpression(newObject, new MemberInitializer[] { initializer });
				context.GhostAssign(settableBase, newObject);
				return true;
			}
			else
			{
				ObjectInitializerExpression initializedObject = ghosted as ObjectInitializerExpression;
				if (initializedObject != null)
				{
					List<MemberInitializer> initializers = new List<MemberInitializer>(initializedObject.Initializers);
					initializers.Add(initializer);
					initializedObject = new ObjectInitializerExpression(initializedObject.NewObjectExpression, initializers);
					context.GhostAssign(settableBase, newObject);
					return true;
				}
				else
				{
					context.GhostAssign(settableBase, ghosted);
					return false;
				}
			}
		}
		private static void HandleStoreField(MsilDecompilationContext context, object operand)
		{
			FieldInfo field = (FieldInfo)operand;
			Expression right = context.PopExpression();
			Expression baseExpression =
				(field.IsStatic)
				? null
				: context.PopExpression()
				;

			if (!field.IsStatic && (field.DeclaringType != null) && field.DeclaringType.IsValueType)
			{
				IndirectExpression indirect = baseExpression as IndirectExpression;
				if (indirect != null)
					baseExpression = indirect.Expression;
			}

			bool initializedMember = TryAppendMemberInitializer(context, baseExpression, new MemberInitializer(field, right));
			//if (!initializedMember)
			//{
			//    //context.GhostAssign(settableBase, ghostedValue);
			//}

			//ISettableExpression settableBase = baseExpression as ISettableExpression;
			//if (settableBase != null)
			//{
			//    Expression ghostedValue = context.UnghostValue(settableBase);
			//    if (ghostedValue != null)
			//    {
			//    }
			//}

			if (!initializedMember)
			{
				FieldReferenceExpression fieldReference = new FieldReferenceExpression(baseExpression, field);
				HandleAssignment(context, fieldReference, right, field.FieldType);
			}
		}
		private static void HandleStoreStaticField(MsilDecompilationContext context, object operand)
		{
			FieldInfo field = (FieldInfo)operand;
			Expression right = context.PopExpression();
			FieldReferenceExpression fieldReference = new FieldReferenceExpression(null, field);
			HandleAssignment(context, fieldReference, right, field.FieldType);
			//Statement statement = new AssignmentStatement(fieldReference, right);
			//context.RecordStatement(statement);
		}
		private static void HandleConvertIntPtr(MsilDecompilationContext context, object operand)
		{
			ConstructorInfo nativeIntConstructor = typeof(IntPtr).GetConstructor(new Type[] { typeof(int) });
			BoundConstructorExpression newObject = new BoundConstructorExpression(nativeIntConstructor, new Expression[] { context.PopExpression() });
			context.PushStackEntry(newObject, typeof(IntPtr));
		}
		private static void HandleConvertByte(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(sbyte));
		}
		private static void HandleConvertShort(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(short));
		}
		private static void HandleConvertInt(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(int));
		}
		private static void HandleConvertLong(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(long));
		}
		private static void HandleConvertUIntPtr(MsilDecompilationContext context, object operand)
		{
			ConstructorInfo nativeUIntConstructor = typeof(UIntPtr).GetConstructor(new Type[] { typeof(uint) });
			BoundConstructorExpression newObject = new BoundConstructorExpression(nativeUIntConstructor, new Expression[] { context.PopExpression() });
			context.PushStackEntry(newObject, typeof(UIntPtr));
		}
		private static void HandleConvertUByte(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(byte));
		}
		private static void HandleConvertUShort(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(ushort));
		}
		private static void HandleConvertUInt(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(uint));
		}
		private static void HandleConvertULong(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(ulong));
		}
		private static void HandleConvertReal32(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(float));
		}
		private static void HandleConvertReal64(MsilDecompilationContext context, object operand)
		{
			HandleCast(context, typeof(double));
		}
		private static void HandleCast(MsilDecompilationContext context, object operand)
		{
			Type targetType = (Type)operand;
			Expression castOperand = context.PopExpression();
			Expression cast = new CastExpression(castOperand, targetType);
			context.PushStackEntry(cast, targetType);
		}
		private static void HandleIsInstance(MsilDecompilationContext context, object operand)
		{
			Type targetType = (Type)operand;
			Expression castOperand = context.PopExpression();
			Expression cast = new CastExpression(castOperand, targetType, true);
			context.PushStackEntry(cast, targetType);
		}
		private static void HandleBranchTrue(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry conditionEntry = context.PopStackEntry();
			Expression condition =
				(Equal(conditionEntry.ExpressionType, typeof(bool)))
				? conditionEntry.Expression
				: Expression.Inequality(conditionEntry.Expression, ConstantExpression.Default(conditionEntry.ExpressionType))
				;
			context.PushStackEntry(condition, typeof(bool));
			//context.ConditionalBranch(((MsilBranchTarget)operand).TargetOffset, true);
		}
		//private static void HandleBranchTrueShort(MsilDisassemblyContext context, object operand)
		//{
		//    //context.ConditionalBranch(((MsilBranchTarget)operand).TargetOffset, true);
		//}
		private static void HandleBranchFalse(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry conditionEntry = context.PopStackEntry();
			Expression condition =
				(Equal(conditionEntry.ExpressionType, typeof(bool)))
				? !conditionEntry.Expression
				: Expression.Equality(conditionEntry.Expression, ConstantExpression.Default(conditionEntry.ExpressionType))
				;
			context.PushStackEntry(condition, typeof(bool));
			//context.ConditionalBranch(((MsilBranchTarget)operand).TargetOffset, false);
		}
		//private static void HandleBranchFalseShort(MsilDisassemblyContext context, object operand)
		//{
		//    //context.ConditionalBranch(((MsilBranchTarget)operand).TargetOffset, false);
		//}
		//protected static void HandleBranch(MsilRecord record, int targetOffset)
		//{
		//    if (this.ExpressionStack.Count > 0)
		//    {
		//        Expression expression = this.PopExpression();
		//        PartialSwitchExpression partialSwitchExpression;
		//        if (this.ExpressionStack.Count > 0)
		//        {
		//            partialSwitchExpression = (PartialSwitchExpression)this.PopExpression();
		//        }
		//        else
		//        {
		//            StatementFrame previousFrame = this.StatementFrames.Last();
		//            this.StatementFrames.Remove(previousFrame);
		//            ConditionalBranchStatement conditionalBranch = (ConditionalBranchStatement)previousFrame.Statement;
		//            partialSwitchExpression = new PartialSwitchExpression(
		//                conditionalBranch.Condition,
		//                conditionalBranch.Criterion,
		//                this.GetLabelOffset(conditionalBranch.Target).Value
		//                );
		//            this.CurrentStatementFrame = previousFrame;
		//        }

		//        if (record.Offset >= partialSwitchExpression.ConditionTargetOffset)
		//        {
		//            Expression trueExpression;
		//            Expression falseExpression;
		//            if (partialSwitchExpression.Criterion)
		//            {
		//                trueExpression = expression;
		//                falseExpression = partialSwitchExpression.FirstPart;
		//            }
		//            else
		//            {
		//                trueExpression = partialSwitchExpression.FirstPart;
		//                falseExpression = expression;
		//            }

		//            SwitchExpression switchExpression = new SwitchExpression(
		//                partialSwitchExpression.Condition,
		//                trueExpression,
		//                falseExpression
		//                );
		//            this.PushExpression(switchExpression);
		//        }
		//        else
		//        {
		//            partialSwitchExpression.FirstPart = expression;
		//            this.PushExpression(partialSwitchExpression);
		//        }
		//    }
		//    else
		//    {
		//        this.RecordStatement(
		//        new BranchStatement(
		//            this.GetLabel(targetOffset)
		//            ),
		//        record.NextOffset
		//        );
		//    }
		//}
		private static void HandleBranch(MsilDecompilationContext context, object operand)
		{
			//context.Branch(((MsilBranchTarget)operand).TargetOffset);
		}
		private static void HandleBranchShort(MsilDecompilationContext context, object operand)
		{
			//context.Branch(((MsilBranchTarget)operand).TargetOffset);
		}
		private static void HandleBranchOnEqual(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			Expression comparison = (Expression.Equality(leftEntry.Expression, right));
			context.PushStackEntry(comparison, typeof(bool));
			HandleBranchTrue(context, operand);
		}
		//private static void HandleBranchOnEqualShort(MsilDisassemblyContext context, object operand)
		//{
		//    Expression rvalue = context.PopExpression();
		//    Expression lvalue = context.PopExpression();
		//    Expression comparison = (Expression.Equality(lvalue, rvalue));
		//    context.PushExpression(comparison);
		//    HandleBranchTrueShort(context, operand);
		//}
		private static void HandleBranchOnNotEqual(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			Expression comparison = (Expression.Inequality(leftEntry.Expression, right));
			context.PushStackEntry(comparison, typeof(bool));
			HandleBranchTrue(context, operand);
		}
		//private static void HandleBranchOnNotEqualShort(MsilDisassemblyContext context, object operand)
		//{
		//    Expression rvalue = context.PopExpression();
		//    Expression lvalue = context.PopExpression();
		//    Expression comparison = (Expression.Inequality(lvalue, rvalue));
		//    context.PushExpression(comparison);
		//    HandleBranchTrueShort(context, operand);
		//}
		private static void HandleBranchOnGreaterOrEqual(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			Expression comparison = (leftEntry.Expression >= right);
			context.PushStackEntry(comparison, typeof(bool));
			HandleBranchTrue(context, operand);
		}
		//private static void HandleBranchOnGreaterOrEqualShort(MsilDisassemblyContext context, object operand)
		//{
		//    Expression rvalue = context.PopExpression();
		//    Expression lvalue = context.PopExpression();
		//    Expression comparison = (lvalue >= rvalue);
		//    context.PushExpression(comparison);
		//    HandleBranchTrueShort(context, operand);
		//}
		private static void HandleBranchOnGreaterThan(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			Expression comparison = (leftEntry.Expression > right);
			context.PushStackEntry(comparison, typeof(bool));
			HandleBranchTrue(context, operand);
		}
		//private static void HandleBranchOnGreaterThanShort(MsilDisassemblyContext context, object operand)
		//{
		//    Expression rvalue = context.PopExpression();
		//    Expression lvalue = context.PopExpression();
		//    Expression comparison = (lvalue > rvalue);
		//    context.PushExpression(comparison, typeof(bool));
		//    HandleBranchTrueShort(context, operand);
		//}
		private static void HandleBranchOnLessOrEqual(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			Expression comparison = (leftEntry.Expression <= right);
			context.PushStackEntry(comparison, typeof(bool));
			HandleBranchTrue(context, operand);
		}
		//private static void HandleBranchOnLessOrEqualShort(MsilDisassemblyContext context, object operand)
		//{
		//    Expression rvalue = context.PopExpression();
		//    Expression lvalue = context.PopExpression();
		//    Expression comparison = (lvalue <= rvalue);
		//    context.PushExpression(comparison);
		//    HandleBranchTrueShort(context, operand);
		//}
		private static void HandleBranchOnLessThan(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry rightEntry = context.PopStackEntry();
			MsilStackEntry leftEntry = context.PopStackEntry();
			Expression right = CoerceExpression(rightEntry.Expression, leftEntry.ExpressionType, context);
			Expression comparison = (leftEntry.Expression < right);
			context.PushStackEntry(comparison, typeof(bool));
			HandleBranchTrue(context, operand);
		}
		//private static void HandleBranchOnLessThanShort(MsilDisassemblyContext context, object operand)
		//{
		//    Expression rvalue = context.PopExpression();
		//    Expression lvalue = context.PopExpression();
		//    Expression comparison = (lvalue < rvalue);
		//    context.PushExpression(comparison);
		//    HandleBranchTrueShort(context, operand);
		//}
		private static void HandleLeave(MsilDecompilationContext context, object operand)
		{
			context.ClearExpressions();
			HandleBranch(context, operand);
		}
		private static void HandleLeaveShort(MsilDecompilationContext context, object operand)
		{
			context.ClearExpressions();
			HandleBranchShort(context, operand);
		}
		private static void HandleSwitch(MsilDecompilationContext context, object operand)
		{
		}
		private static void HandleThrow(MsilDecompilationContext context, object operand)
		{
			MsilStackEntry exceptionEntry = context.PopStackEntry();
			context.RecordStatement(new ThrowStatement(exceptionEntry.Expression));
		}
		private static void HandleRethrow(MsilDecompilationContext context, object operand)
		{
			context.RecordStatement(new ThrowStatement());
		}
		private static void HandleLoadToken(MsilDecompilationContext context, object operand)
		{
			MemberInfo member = (MemberInfo)operand;
			Type handleType = null;

			FieldInfo field = member as FieldInfo;
			if (field != null)
			{
				handleType = typeof(RuntimeFieldHandle);
			}
			else
			{
				Type type = member as Type;
				if (type != null)
				{
					handleType = typeof(RuntimeTypeHandle);
				}
				else
				{
					MethodBase method = member as MethodBase;
					if (method != null)
					{
						handleType = typeof(RuntimeMethodHandle);
					}
					else
					{
						throw new ArgumentException("Unknown handle type");
					}
				}
			}

			context.PushStackEntry(new MetadataTokenExpression(member), handleType);
		}
		#endregion

		#region Expression helpers
		private static long[] GetEnumValues(Type enumType)
		{
			Array enumValues = enumType.GetEnumValues();
			long[] numericValues = new long[enumValues.Length];

			if (enumValues.Length == 0)
				return numericValues;

			// Since the enum may or may not be a runtime type,
			// use the first element to determine the type

			Type underlyingType = enumValues.GetValue(0).GetType();
			TypeCode typeCode = Type.GetTypeCode(underlyingType);
			bool isSigned = false;
			switch (typeCode)
			{
				case TypeCode.SByte:
				case TypeCode.Int16:
				case TypeCode.Int32:
				case TypeCode.Int64:
					isSigned = true;
					break;
			}

			if (isSigned)
			{
				for (int i = 0; i < enumValues.Length; i++)
				{
					numericValues[i] = Convert.ToInt64(enumValues.GetValue(i));
				}
			}
			else
			{
				for (int i = 0; i < enumValues.Length; i++)
				{
					numericValues[i] = (long)Convert.ToUInt64(enumValues.GetValue(i));
				}
			}

			return numericValues;
		}
		private static Expression CoerceEnumExpression(int value, Type enumType)
		{
			string enumName = Enum.GetName(enumType, value);
			if (enumName != null)
			{
				return new FieldReferenceExpression(null, (enumType.GetField(enumName)));
			}
			else
			{
				long remainingEnumValue = (uint)value;

				bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), true);
				if (!isFlags || (remainingEnumValue == 0))
				{
					return new CastExpression(ConstantExpression.FromValue((int)value), enumType);
				}
				else
				{
					long[] enumValues = GetEnumValues(enumType);

					Array.Reverse(enumValues);

					Expression enumExpression = null;
					foreach (var itemNum in enumValues)
					{
						object item = itemNum;
						if ((remainingEnumValue & itemNum) == itemNum)
						{
							remainingEnumValue &= ~itemNum;

							string itemName = Enum.GetName(enumType, item);

							Expression itemExpression = new FieldReferenceExpression(null, (enumType.GetField(itemName)));
							if (enumExpression == null)
								enumExpression = itemExpression;
							else
								enumExpression |= itemExpression;
						}
					}

					if (remainingEnumValue != 0)
					{
					}

					return enumExpression;
				}
			}
		}
		internal static Expression CoerceExpression(Expression expression, Type targetType, MsilDecompilationContext context)
		{
			Expression coerced = null;

			if (
				(context != null)
				&& targetType.IsSubclassOf(typeof(LinqExpressions.LambdaExpression))
				)
			{
				try
				{
					coerced = TryGetLambda(expression, context);
				}
				catch
				{ // Code still in testing
				}
			}

			if (coerced == null)
			{
				if (
					targetType.IsEnum
					&& (expression is IntegerConstantExpression)
					)
				{
					coerced = CoerceEnumExpression(((IntegerConstantExpression)expression).TypedValue, targetType);
				}
				else
				{
					coerced = expression.CoerceTo(targetType);
				}
			}

			Debug.Assert(coerced != null);

			return coerced;
		}

		private static Expression TryGetLambda(Expression expression, MsilDecompilationContext context)
		{
			MethodInfo method;
			Expression[] arguments;

			if (!LinqConverter.TryGetMethodCallInfo(expression, out method, out arguments)) return null;

			bool isLambdaMethod =
				(method != null)
				&& (method.Name == "Lambda")
				&& (Equal(method.ReflectedType, typeof(LinqExpression)))
				&& (method.IsGenericMethod)
				;

			if (!isLambdaMethod) return null;

			// Get leading parameter declarations
			//Dictionary<Variable, VariableDeclarationStatement> parameterDeclarationTable = new Dictionary<Variable, VariableDeclarationStatement>();
			//Dictionary<Variable, TypedVariable> parameterVariableTable = new Dictionary<Variable, TypedVariable>();
			//for (int i = context.Statements.Count - 1; i >= 0; i--)
			//{
			//    VariableDeclarationStatement declaration = context.Statements[i] as VariableDeclarationStatement;
			//    if (declaration == null) break;

			//    TypedVariable parameter = TryRaiseLambdaParameter(declaration.Initializer);
			//    if (parameter == null) break;

			//    parameterDeclarationTable[declaration.Variable] = declaration;
			//    parameterVariableTable[declaration.Variable] = parameter;
			//}

			// Get parameters
			Expression bodyExpression = arguments[0];
			Expression parametersExpression = arguments[1];
			Expression[] parameterExpressions = null;
			{
				NewArrayExpression parametersArray = parametersExpression as NewArrayExpression;
				if (parametersArray != null)
				{
					parameterExpressions = new Expression[0];
				}
				else
				{
					NewImplicitArrayExpression implicitArray = parametersExpression as NewImplicitArrayExpression;
					if (implicitArray != null)
					{
						parameterExpressions = implicitArray.Arguments.ToArray();
					}
				}
			}

			if (parameterExpressions == null) return null;

			List<TypedVariable> parameters = new List<TypedVariable>();
			Dictionary<Expression, Expression> parameterTable = new Dictionary<Expression, Expression>();
			foreach (var parameterExpression in parameterExpressions)
			{
				BoundVariableReferenceExpression variableRef = parameterExpression as BoundVariableReferenceExpression;
				if (variableRef != null)
				{
					LocalVariable localVar = variableRef.Variable as LocalVariable;
					if (localVar != null)
					{
						TypedVariable parameter;
						if (!context.LinqParameterTable.TryGetValue(localVar, out parameter)) return null;
						parameters.Add(parameter);
						parameterTable[variableRef] = new BoundVariableReferenceExpression(parameter);
					}
				}
			}

			LinqConversionContext linqContext = new LinqConversionContext(parameterTable);
			Expression raisedBody = LinqConverter.TryRaiseLinqExpression(linqContext, bodyExpression);

			if (raisedBody != null)
			{
				LambdaExpression lambda = new LambdaExpression(raisedBody, parameters.ToArray());
				return lambda;
			}

			//for (int i = 0; i < parameterExpressions.Length; i++)
			//{
			//    Expression parameterExpression = parameterExpressions[i];
			//    BoundVariableReferenceExpression variableRef = parameterExpression as BoundVariableReferenceExpression;
			//    if (variableRef == null) return null;

			//}

			return null;
		}

		#endregion

		static MsilDecompiler()
		{
			InitializeOpCodeHandlers();
		}

		private static void ScanAttributeArgumentDependencies(
			Module module,
			CustomAttributeTypedArgument argument,
			IList<MsilDependency> dependencies
			)
		{
			string str = argument.Value as string;
			if (str != null)
			{
				dependencies.Add(new MsilStringDependency(module, str, 0));
			}
			else
			{
				Array array = argument.Value as Array;
				foreach (var element in array)
				{
					string stringElement = element as string;
					if (stringElement != null)
						dependencies.Add(new MsilStringDependency(module, stringElement, 0));
				}
			}
		}
		private static void ScanAttributeDependencies(
			MemberInfo member,
			IList<MsilDependency> dependencies
			)
		{
			IList<CustomAttributeData> attributes = member.GetCustomAttributesData();
			foreach (var attribute in attributes)
			{
				foreach (var argument in attribute.ConstructorArguments)
				{
					ScanAttributeArgumentDependencies(member.Module, argument, dependencies);
				}
				foreach (var argument in attribute.NamedArguments)
				{
					ScanAttributeArgumentDependencies(member.Module, argument.TypedValue, dependencies);
				}
			}
		}
		public static IEnumerable<MsilDependency> GetDependenciesOf(Type type)
		{
			if (type == null) throw new ArgumentNullException("type");

			List<MsilDependency> dependencies = new List<MsilDependency>();
			ScanAttributeDependencies(type, dependencies);

			return dependencies;
		}
		public static IEnumerable<MsilDependency> GetDependenciesOf(MethodBase method)
		{
			if (method == null) throw new ArgumentNullException("method");

			List<MsilDependency> dependencies = new List<MsilDependency>();
			{
				MethodInfo actualMethod = method as MethodInfo;
				if (actualMethod != null)
				{
					dependencies.Add(MsilTypeDependency.FromType(actualMethod.ReturnType, MsilTypeUsage.ReturnValue));
				}
			}
			ParameterInfo[] parameters = method.GetParameters();
			foreach (var parameter in parameters)
			{
				try
				{
					Type paramType = parameter.ParameterType;
					dependencies.Add(MsilTypeDependency.FromType(paramType, MsilTypeUsage.ParameterDeclaration));
				}
				catch { }
			}
			{
				MethodBody body = method.GetMethodBody();
				if (body != null)
				{
					foreach (var local in body.LocalVariables)
					{
						dependencies.Add(MsilTypeDependency.FromType(local.LocalType, MsilTypeUsage.LocalDeclaration));
					}

					MsilReader reader = new MsilReader(
						method,
						MsilReaderOptions.MemberReferencesOnly,
						body
						);
					while (reader.MoveNext())
					{
						OpcodeHandler handler;
						if (OpcodeHandlerTable.TryGetValue(reader.OpCode.Value, out handler))
						{
							var instructionDependencies = handler.GetDependencies(method, reader.Operand);
							if (instructionDependencies != null)
								dependencies.AddRange(instructionDependencies);
						}
					}
				}
			}

			return dependencies;
		}
		public static bool HasAnyDependency(MethodBase method, params MethodBase[] dependencies)
		{
			if (method == null) throw new ArgumentNullException("method");
			if (dependencies == null) throw new ArgumentNullException("dependencies");
			if (dependencies.Length == 0) throw new ArgumentException("Caller must specify at least one dependency", "dependencies");

			RuntimeMethodHandle[] dependencyHandles = Array.ConvertAll(dependencies, m => m.MethodHandle);

			bool hasDependency = false;
			{
				MethodBody body = method.GetMethodBody();
				if (body != null)
				{
					MsilReader reader = new MsilReader(method, MsilReaderOptions.MemberReferencesOnly);
					while (reader.MoveNext())
					{
						RuntimeMethodHandle? handle = reader.Operand as RuntimeMethodHandle?;
						hasDependency =
							handle.HasValue
							&& dependencyHandles.Contains(handle.Value)
							;
						//if (object.Equals(reader.Operand, dependencyHandle))
						//{
						//    hasDependency = true;
						//    break;
						//}
					}
				}
			}

			return hasDependency;
		}

		public static MethodDecompilation Decompile(MethodBase method)
		{
			return Decompile(method, MsilDecompilationFlags.Full);
		}
		public static MethodDecompilation Decompile(MethodBase method, MsilDecompilationFlags flags)
		{
			if (method == null) throw new ArgumentNullException("method");

			List<Instruction> instructions = GetMsilInstructions(method);

			//List<MsilBlock> blocks=new List<MsilBlock>();
			//MsilBlock currentBlock=null;

			MsilDecompilationContext context = new MsilDecompilationContext(method, instructions, HandleInstruction);


			List<MsilTransition> transitions = context.Transitions;
			int branchCount = 0;
			LocalVariable loadedVariable = null;
			foreach (var instruction in instructions)
			{
				object operand = instruction.Operand;

				if (loadedVariable != null)
				{
					if (instruction.OpCode.FlowControl == FlowControl.Cond_Branch)
					{
						if (loadedVariable.Usage == LocalVariableUsage.Unknown)
						{
							loadedVariable.Usage = LocalVariableUsage.Decision;
						}
					}
					else
					{
						loadedVariable.Usage = LocalVariableUsage.Normal;
					}

					loadedVariable = null;
				}

				if (
					(instruction.OpCode == OpCodes.Ldloc)
					|| (instruction.OpCode == OpCodes.Ldloc_0)
					|| (instruction.OpCode == OpCodes.Ldloc_1)
					|| (instruction.OpCode == OpCodes.Ldloc_2)
					|| (instruction.OpCode == OpCodes.Ldloc_3)
					|| (instruction.OpCode == OpCodes.Ldloc_S)
					|| (instruction.OpCode == OpCodes.Ldloca)
					|| (instruction.OpCode == OpCodes.Ldloca_S)
					)
				{
					int variableIndex;
					if (
						(instruction.OpCode == OpCodes.Ldloc)
						|| (instruction.OpCode == OpCodes.Ldloca)
						)
						variableIndex = (int)instruction.Operand;
					else if (instruction.OpCode == OpCodes.Ldloc_0)
						variableIndex = 0;
					else if (instruction.OpCode == OpCodes.Ldloc_1)
						variableIndex = 1;
					else if (instruction.OpCode == OpCodes.Ldloc_2)
						variableIndex = 2;
					else if (instruction.OpCode == OpCodes.Ldloc_3)
						variableIndex = 3;
					else /*if (
						(instruction.OpCode == OpCodes.Ldloc_S)
						|| (instruction.OpCode == OpCodes.Ldloca_S)
						)*/
						variableIndex = (int)(byte)instruction.Operand;

					loadedVariable = context.LocalVariables[variableIndex];
					MsilVariableReferenceType referenceType = MsilVariableReferenceType.Load;
					if (
						(instruction.OpCode == OpCodes.Ldloca)
						|| (instruction.OpCode == OpCodes.Ldloca_S)
						)
					{
						referenceType = MsilVariableReferenceType.Reference;
						context.VariableReferences.Add(new MsilVariableReference(loadedVariable, instruction.Offset, referenceType));
						loadedVariable.Usage = LocalVariableUsage.Normal;
						loadedVariable = null;
					}
					else
					{
						context.VariableReferences.Add(new MsilVariableReference(loadedVariable, instruction.Offset, referenceType));
					}
				}
				if (
					(instruction.OpCode == OpCodes.Stloc)
					|| (instruction.OpCode == OpCodes.Stloc_0)
					|| (instruction.OpCode == OpCodes.Stloc_1)
					|| (instruction.OpCode == OpCodes.Stloc_2)
					|| (instruction.OpCode == OpCodes.Stloc_3)
					|| (instruction.OpCode == OpCodes.Stloc_S)
					)
				{
					int variableIndex;
					if (instruction.OpCode == OpCodes.Stloc)
						variableIndex = (int)instruction.Operand;
					else if (instruction.OpCode == OpCodes.Stloc_0)
						variableIndex = 0;
					else if (instruction.OpCode == OpCodes.Stloc_1)
						variableIndex = 1;
					else if (instruction.OpCode == OpCodes.Stloc_2)
						variableIndex = 2;
					else if (instruction.OpCode == OpCodes.Stloc_3)
						variableIndex = 3;
					else /*if (instruction.OpCode == OpCodes.Stloc_S)*/
						variableIndex = (int)(byte)instruction.Operand;

					LocalVariable variable = context.LocalVariables[variableIndex];
					context.VariableReferences.Add(new MsilVariableReference(variable, instruction.Offset, MsilVariableReferenceType.Store));
				}

				switch (instruction.OpCode.FlowControl)
				{
					case FlowControl.Cond_Branch:
					case FlowControl.Branch:
						{
							MsilBranchTarget[] targets = operand as MsilBranchTarget[];
							if (targets != null)
							{
								branchCount += targets.Length;
								MsilSwitch swichTransition = new MsilSwitch(instruction.Offset, targets);
								transitions.Add(swichTransition);
								foreach (var branchTarget in targets)
								{
									int targetOffset = branchTarget.TargetOffset;
									context.MarkBlockStartOffset(targetOffset);
								}
							}
							else
							{
								branchCount++;
								MsilBranchTarget target = (MsilBranchTarget)operand;
								context.MarkBlockStartOffset(target.TargetOffset);
								MsilBranch branch = new MsilBranch(
									instruction.Offset,
									instruction.OpCode.FlowControl == FlowControl.Cond_Branch,
									target.TargetOffset
									);
								transitions.Add(branch);
							}
							context.MarkBlockStartOffset(instruction.NextOffset);
						}
						break;
					//case FlowControl.Break:
					//case FlowControl.Call:
					//case FlowControl.Return:
					//case FlowControl.Throw:
					//    {
					//        MsilTransition transition = new MsilTransition(record.Offset, record.OpCode.FlowControl);
					//        transitions.Add(transition);
					//        context.MarkBlockStartOffset(record.NextOffset);
					//    }
					//    break;
				}
			}

			// Mark exception handling blocks
			var exceptionHandlingClauses = context.MethodBody.ExceptionHandlingClauses;
			foreach (var exceptionHandlingClause in exceptionHandlingClauses)
			{
				context.MarkBlockStartOffset(exceptionHandlingClause.TryOffset);
				context.MarkBlockStartOffset(exceptionHandlingClause.HandlerOffset);
			}

			IEnumerable<Statement> statements = null;
			bool includeSource = flags.HasFlag(MsilDecompilationFlags.IncludeSourceCode);
			int methodLength = instructions.Last().NextOffset;
			ConstructorInfo chainedConstructor = null;
			IEnumerable<Expression> chainedArguments = null;
			bool initializesObject = false;
			if (includeSource)
			{
				MethodFlowAnalyzer methodAnalyzer =
					(method is ConstructorInfo)
					? new ConstructorFlowAnalyzer(context, methodLength)
					: new MethodFlowAnalyzer(context, methodLength);
				context.Advance();
				try
				{
					methodAnalyzer.Analyze();
					statements = methodAnalyzer.Statements;

					ConstructorFlowAnalyzer constructorAnalyzer = methodAnalyzer as ConstructorFlowAnalyzer;
					if (constructorAnalyzer != null)
					{
						chainedConstructor = constructorAnalyzer.Constructor;
						chainedArguments = constructorAnalyzer.ConstructorArguments;
						initializesObject = constructorAnalyzer.InitializesObject;
					}
				}
				catch (Exception ex)
				{
				}
			}

			int blockCount = context.BlockStartOffsets.Count();

			MsilMetrics metrics = new MsilMetrics(
				methodLength,
				blockCount,
				context.StatementCount,
				branchCount
				);

			MethodDecompilation result = new MethodDecompilation(
				method,
				instructions,
				statements,
				context.StatementFrameTable,
				chainedConstructor,
				chainedArguments,
				initializesObject
			);

			// Complete variable names
			foreach (var variable in context.LocalVariables)
			{
				if (MsilDecompiler.IsVariableUnnamed(variable))
				{
					EnsureVariableNamed(context, variable, SuggestNameHint(null, variable.StaticVariableType));
				}
			}

			result.Metrics[CodeMetricDefinition.LinesOfCode] = context.StatementCount;
			result.Metrics[CodeMetricDefinition.MethodLength] = methodLength;
			result.Metrics[CodeMetricDefinition.BranchCount] = branchCount;

			return result;
		}

		public static bool HasMethodBody(MethodBase method)
		{
			if (method == null) throw new ArgumentNullException("method");

			MethodImplAttributes implAttributes = method.GetMethodImplementationFlags();
			MethodAttributes attributes = method.Attributes;
			bool hasMethodBody =
				((implAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.IL)
				&& (!implAttributes.HasFlag(MethodImplAttributes.InternalCall))
				&& !attributes.HasFlag(MethodAttributes.Abstract)
				&& !attributes.HasFlag(MethodAttributes.PinvokeImpl)
				&& (
					(method.DeclaringType == null)
					|| (!method.DeclaringType.IsInterface)
					)
				;
			return hasMethodBody;
			//return (method.GetMethodBody() != null);
		}

		public static List<Instruction> GetMsilInstructions(MethodBase method)
		{
			if (method == null) throw new ArgumentNullException("method");

			int stackSize = 0;
			List<Instruction> instructions = new List<Instruction>();
			MsilReader reader = new MsilReader(method);
			while (reader.MoveNext())
			{
				OpCode opcode = reader.OpCode;
				int instructionOffset = reader.InstructionOffset;
				int instructionLength = reader.InstructionLength;
				object operand = reader.Operand;

				//if ((opcode.OpCodeType == OpCodeType.Macro) && (opcode.OperandType == OperandType.InlineNone))
				//{
				//    ExpandMacroOpcode(ref opcode, ref operand);
				//}

				int inputCount = 0;
				int outputCount = 0;
				if (opcode.OpCodeType != OpCodeType.Prefix)
				{
					//if (OpcodeHandlerTable.TryGetValue(opcode.Value, out handler))
					//{
					//    //if (!OpcodeHandlerTable.TryGetValue(opcode, out handler)) throw new UnrecognizedOpCodeException(opcode, operand);
					//    inputCount = handler.GetInputCount(method, operand);
					//    outputCount = handler.GetOutputCount(method, operand);
					//}
				}
				else
				{
					inputCount = outputCount = 0;
				}
				Instruction instruction = new Instruction(
					instructionOffset,
					instructionLength,
					opcode,
					stackSize,
					inputCount,
					outputCount,
					operand
					);
				instructions.Add(instruction);
				stackSize -= inputCount;
				stackSize += outputCount;
			}

			return instructions;
		}

		[Obsolete("Decomplier now handles macro instructions directly.", true)]
		private static void ExpandMacroOpcode(ref OpCode opcode, ref object value)
		{
			if (opcode == OpCodes.Ldarg_0) { opcode = OpCodes.Ldarg; value = 0; }
			else if (opcode == OpCodes.Ldarg_1) { opcode = OpCodes.Ldarg; value = 1; }
			else if (opcode == OpCodes.Ldarg_2) { opcode = OpCodes.Ldarg; value = 2; }
			else if (opcode == OpCodes.Ldarg_3) { opcode = OpCodes.Ldarg; value = 3; }
			//if (opcode == OpCodes.Ldarg_0) { opcode = OpCodes.Ldarg; value = parameters[0]; }
			//else if (opcode == OpCodes.Ldarg_1) { opcode = OpCodes.Ldarg; value = parameters[1]; }
			//else if (opcode == OpCodes.Ldarg_2) { opcode = OpCodes.Ldarg; value = parameters[2]; }
			//else if (opcode == OpCodes.Ldarg_3) { opcode = OpCodes.Ldarg; value = parameters[3]; }
			else if (opcode == OpCodes.Ldloc_0) { opcode = OpCodes.Ldloc; value = 0; }
			else if (opcode == OpCodes.Ldloc_1) { opcode = OpCodes.Ldloc; value = 1; }
			else if (opcode == OpCodes.Ldloc_2) { opcode = OpCodes.Ldloc; value = 2; }
			else if (opcode == OpCodes.Ldloc_3) { opcode = OpCodes.Ldloc; value = 3; }
			else if (opcode == OpCodes.Stloc_0) { opcode = OpCodes.Stloc; value = 0; }
			else if (opcode == OpCodes.Stloc_1) { opcode = OpCodes.Stloc; value = 1; }
			else if (opcode == OpCodes.Stloc_2) { opcode = OpCodes.Stloc; value = 2; }
			else if (opcode == OpCodes.Stloc_3) { opcode = OpCodes.Stloc; value = 3; }
			else if (opcode == OpCodes.Ldc_I4_0) { opcode = OpCodes.Ldc_I4; value = 0; }
			else if (opcode == OpCodes.Ldc_I4_1) { opcode = OpCodes.Ldc_I4; value = 1; }
			else if (opcode == OpCodes.Ldc_I4_2) { opcode = OpCodes.Ldc_I4; value = 2; }
			else if (opcode == OpCodes.Ldc_I4_3) { opcode = OpCodes.Ldc_I4; value = 3; }
			else if (opcode == OpCodes.Ldc_I4_4) { opcode = OpCodes.Ldc_I4; value = 4; }
			else if (opcode == OpCodes.Ldc_I4_5) { opcode = OpCodes.Ldc_I4; value = 5; }
			else if (opcode == OpCodes.Ldc_I4_6) { opcode = OpCodes.Ldc_I4; value = 6; }
			else if (opcode == OpCodes.Ldc_I4_7) { opcode = OpCodes.Ldc_I4; value = 7; }
			else if (opcode == OpCodes.Ldc_I4_8) { opcode = OpCodes.Ldc_I4; value = 8; }
			else if (opcode == OpCodes.Ldc_I4_M1) { opcode = OpCodes.Ldc_I4; value = -1; }
			else
			{
				throw new ArgumentException();
			}
		}


		//private static IEnumerable<Statement> GetStatements(MsilDecompilerContext context)
		//{
		//    List<Variable> declaredVariables = new List<Variable>();

		//    MsilMethodFlowAnalyzer methodFlow = new MsilMethodFlowAnalyzer(context);
		//    MsilFlowAnalyzer flow = methodFlow;
		//    Stack<MsilFlowAnalyzer> flowStack = new Stack<MsilFlowAnalyzer>();

		//    foreach (var block in context.Blocks)
		//    {
		//        MsilFlowAnalyzer newFlow = flow.AnalyzeBlock(block);
		//        if (newFlow == null)
		//        {
		//            flow = flowStack.Pop();
		//        }
		//        else if (newFlow != flow)
		//        {
		//            flowStack.Push(flow);
		//            flow = newFlow;
		//        }
		//    }

		//    return methodFlow.Statements;
		//}

		private static void HandleInstruction(MsilDecompilationContext context)
		{
			Instruction instruction = context.CurrentInstruction;
			OpcodeHandler handler;

			if (instruction.OpCode.OpCodeType == OpCodeType.Prefix) return;
			if (!OpcodeHandlerTable.TryGetValue(instruction.OpCode.Value, out handler)) throw new UnrecognizedOpCodeException(instruction.OpCode, instruction.Operand);

			handler.Handle(context, instruction.Operand);
		}
	}
}
